8.5btc / app.py
aianyu's picture
Upload 5 files
78f3214 verified
import streamlit as st
import subprocess
import tempfile
import os
import shutil
# --- Configuration ---
APP_TITLE = "Bitcoin Wallet.dat Password Recovery (btcrecover)"
APP_INTRO = """
Upload your `wallet.dat` file and a password list to attempt recovery.
This tool uses `btcrecover` in the backend.
"""
WARNING_TEXT = """
**⚠️ Important Considerations for Hugging Face Spaces:**
- **Resource Limits:** Hugging Face Spaces (especially free tier) have CPU, memory, and execution time limits.
- **Execution Time:** Password recovery can be very time-consuming. Long-running jobs might be terminated. This tool is best for very small password lists or educational purposes on this platform.
- **Security:** Your `wallet.dat` file is uploaded to the server for processing. While not stored permanently by this app, please be aware of this.
- **Patience:** The process can be slow. Output will stream below.
"""
def find_btcrecover_executable():
"""Tries to find the btcrecover executable."""
executable = shutil.which("btcrecover")
if executable:
return executable
# Fallback for some environments, though `shutil.which` should work in Docker with PATH set
possible_paths = [
"/usr/local/bin/btcrecover",
os.path.expanduser("~/.local/bin/btcrecover")
]
for path in possible_paths:
if os.path.exists(path) and os.access(path, os.X_OK):
return path
return None
def run_btcrecover(wallet_path, password_list_path, btcrecover_exe, extra_args=None):
"""Runs btcrecover and streams its output."""
command = [
btcrecover_exe,
"--walletfile", wallet_path,
"--passwordlist", password_list_path,
]
if extra_args:
command.extend(extra_args)
st.info(f"Executing: {' '.join(command)}")
# Placeholder for real-time output
output_placeholder = st.empty()
log_output = ""
try:
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # Combine stdout and stderr
text=True,
bufsize=1, # Line buffered
universal_newlines=True
)
if process.stdout:
for line in iter(process.stdout.readline, ''):
log_output += line
output_placeholder.code(log_output, language="text") # Update placeholder with cumulative log
if "Password found:" in line:
st.balloons()
st.success(f"πŸŽ‰ Password potentially found! Check logs above. Line: {line.strip()}")
elif "Password not found" in line and "exhaustive" in line:
st.warning("Password not found in the list after exhaustive search by btcrecover.")
process.stdout.close()
return_code = process.wait()
if return_code != 0 and "Password found:" not in log_output:
st.error(f"btcrecover exited with error code {return_code}. Check the logs.")
elif "Password found:" not in log_output and ("Password not found" not in log_output or "exhaustive" not in log_output) :
# If no "Password found" and no clear "not found" message from btcrecover, it might be an unknown state or early exit
st.info("btcrecover finished. Please review the logs above to determine the outcome.")
except FileNotFoundError:
st.error(f"Error: btcrecover executable not found at '{btcrecover_exe}'. Ensure it's installed and in PATH.")
except Exception as e:
st.error(f"An unexpected error occurred: {str(e)}")
st.code(log_output, language="text") # Show any logs gathered so far
# --- Streamlit UI ---
st.set_page_config(page_title=APP_TITLE, layout="wide")
st.title(APP_TITLE)
st.markdown(APP_INTRO)
st.warning(WARNING_TEXT)
btcrecover_exe = find_btcrecover_executable()
if not btcrecover_exe:
st.error("`btcrecover` executable not found in the environment. The application cannot proceed.")
st.stop()
else:
st.sidebar.success(f"btcrecover found: {btcrecover_exe}")
# File Uploaders
st.sidebar.header("1. Upload Files")
uploaded_wallet_file = st.sidebar.file_uploader("Upload wallet.dat", type=['dat', 'wallet'])
uploaded_password_list = st.sidebar.file_uploader("Upload password list (text file, one password per line)", type=['txt', 'list'])
# Or provide passwords directly in a textarea
st.sidebar.markdown("--- OR ---")
passwords_text_area = st.sidebar.text_area("Paste passwords (one per line)", height=150,
help="If you provide passwords here, the uploaded password list will be ignored.")
st.sidebar.header("2. Options")
no_strict_verify = st.sidebar.checkbox("Disable strict wallet verification (`--no-strict-wallet-verify`)",
help="Useful for very old or slightly non-standard wallet.dat files.")
# processes_auto = st.sidebar.checkbox("Use multiple processes if available (`--processes auto`)",
# help="Might be limited by Hugging Face Spaces resources.")
if st.sidebar.button("πŸš€ Start Recovery Attempt"):
if not uploaded_wallet_file:
st.error("Please upload a wallet.dat file.")
elif not uploaded_password_list and not passwords_text_area.strip():
st.error("Please upload a password list file OR paste passwords into the text area.")
else:
with tempfile.NamedTemporaryFile(delete=False, suffix=".dat") as tmp_wallet, \
tempfile.NamedTemporaryFile(mode="w+", delete=False, suffix=".txt") as tmp_passlist:
# Save uploaded wallet to a temporary file
tmp_wallet.write(uploaded_wallet_file.getvalue())
tmp_wallet_path = tmp_wallet.name
# Prepare password list
if passwords_text_area.strip():
tmp_passlist.write(passwords_text_area.strip())
else:
tmp_passlist.write(uploaded_password_list.getvalue().decode())
tmp_passlist_path = tmp_passlist.name
# Ensure files are closed before btcrecover tries to access them
tmp_wallet.close()
tmp_passlist.close()
st.info(f"Wallet file saved to: {tmp_wallet_path}")
st.info(f"Password list saved to: {tmp_passlist_path}")
extra_args = []
if no_strict_verify:
extra_args.append("--no-strict-wallet-verify")
# if processes_auto: # Be cautious with this on shared resources
# extra_args.append("--processes")
# extra_args.append("auto")
with st.spinner("Attempting password recovery... Please wait. This can take a very long time."):
run_btcrecover(tmp_wallet_path, tmp_passlist_path, btcrecover_exe, extra_args)
# Clean up temporary files
try:
os.unlink(tmp_wallet_path)
os.unlink(tmp_passlist_path)
st.caption(f"Cleaned up temporary files: {os.path.basename(tmp_wallet_path)}, {os.path.basename(tmp_passlist_path)}")
except Exception as e:
st.warning(f"Could not clean up temporary files: {e}")
else:
st.info("Upload your files and click 'Start Recovery Attempt' in the sidebar.")
st.markdown("---")
st.markdown("Powered by [btcrecover](https://github.com/gurnec/btcrecover) and [Streamlit](https://streamlit.io/).")
st.markdown("Remember the limitations when running on free cloud platforms.")