Files
p2pk-bf/bruteforce/extract_p2pk_utxo.py

243 lines
7.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Script per estrarre chiavi pubbliche P2PK con UTXO non spesi
dal database SQLite generato dallo scanner
"""
import sqlite3
import sys
import os
def extract_p2pk_unspent(db_path, output_file):
"""
Estrae le chiavi pubbliche P2PK non spese dal database
e le salva in un file di testo (una per riga)
"""
# Verifica esistenza database
if not os.path.exists(db_path):
print(f"[ERROR] Database non trovato: {db_path}")
print("[!] Esegui prima lo scanner Python per creare il database")
return False
try:
# Connetti al database
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Query per ottenere solo P2PK non spesi
query = """
SELECT
scriptpubkey,
block_height,
txid,
output_index,
value_satoshi
FROM p2pk_addresses
WHERE is_unspent = 1
ORDER BY value_satoshi DESC
"""
cursor.execute(query)
results = cursor.fetchall()
if not results:
print("[!] Nessun P2PK non speso trovato nel database")
conn.close()
return False
print(f"[+] Trovati {len(results)} P2PK non spesi")
print("[+] Estrazione in corso...")
# Estrai le chiavi pubbliche
pubkeys = []
total_value = 0
with open(output_file, 'w') as f:
# Header
f.write("# Bitcoin P2PK Public Keys (Unspent)\n")
f.write("# Format: One public key per line (hex, uncompressed)\n")
f.write("# Generated from database\n")
f.write("#\n")
for row in results:
scriptpubkey, block_height, txid, output_index, value_sat = row
# Il scriptpubkey è nel formato: 41<pubkey>ac o 21<pubkey>ac
# Dobbiamo estrarre solo la pubkey
# Rimuovi il primo byte (41 o 21) e l'ultimo byte (ac - OP_CHECKSIG)
if len(scriptpubkey) == 134: # Non compresso: 41 + 130 char (65 bytes) + ac
pubkey = scriptpubkey[2:-2] # Rimuovi 41 e ac
elif len(scriptpubkey) == 70: # Compresso: 21 + 66 char (33 bytes) + ac
pubkey = scriptpubkey[2:-2] # Rimuovi 21 e ac
# NOTA: Il bruteforce genera solo pubkey non compresse
# quindi le chiavi compresse non verranno trovate
print(f"[!] SKIP chiave compressa: {txid}:{output_index}")
continue
else:
print(f"[!] SKIP formato sconosciuto: {scriptpubkey}")
continue
# Aggiungi il prefisso 04 se non c'è (formato non compresso)
if not pubkey.startswith('04'):
pubkey = '04' + pubkey
# Verifica lunghezza corretta (130 caratteri hex = 65 bytes)
if len(pubkey) != 130:
print(f"[!] SKIP lunghezza errata ({len(pubkey)}): {txid}:{output_index}")
continue
# Scrivi la pubkey nel file
f.write(f"{pubkey}\n")
pubkeys.append(pubkey)
total_value += value_sat
# Info dettagliate
btc_value = value_sat / 100000000.0
print(f" [{len(pubkeys)}] Block {block_height} | {btc_value:.8f} BTC | {txid[:16]}...:{output_index}")
conn.close()
# Statistiche finali
print("\n" + "="*60)
print(" ESTRAZIONE COMPLETATA")
print("="*60)
print(f"Chiavi pubbliche estratte: {len(pubkeys)}")
print(f"Valore totale: {total_value / 100000000.0:.8f} BTC")
print(f"Valore totale: {total_value:,} satoshi")
print(f"File output: {output_file}")
print("="*60)
return True
except sqlite3.Error as e:
print(f"[ERROR] Errore database: {e}")
return False
except Exception as e:
print(f"[ERROR] Errore: {e}")
return False
def show_stats(db_path):
"""
Mostra statistiche sui P2PK nel database
"""
if not os.path.exists(db_path):
print(f"[ERROR] Database non trovato: {db_path}")
return
try:
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Statistiche generali
cursor.execute("SELECT COUNT(*) FROM p2pk_addresses")
total_p2pk = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM p2pk_addresses WHERE is_unspent = 1")
unspent_p2pk = cursor.fetchone()[0]
cursor.execute("SELECT SUM(value_satoshi) FROM p2pk_addresses")
total_value = cursor.fetchone()[0] or 0
cursor.execute("SELECT SUM(value_satoshi) FROM p2pk_addresses WHERE is_unspent = 1")
unspent_value = cursor.fetchone()[0] or 0
print("\n" + "="*60)
print(" STATISTICHE DATABASE P2PK")
print("="*60)
print(f"Totale P2PK: {total_p2pk}")
print(f"P2PK non spesi: {unspent_p2pk}")
print(f"P2PK spesi: {total_p2pk - unspent_p2pk}")
print(f"-" * 60)
print(f"Valore totale: {total_value / 100000000.0:.8f} BTC")
print(f"Valore non speso: {unspent_value / 100000000.0:.8f} BTC")
print(f"Valore speso: {(total_value - unspent_value) / 100000000.0:.8f} BTC")
print("="*60)
# Top 10 P2PK non spesi per valore
print("\nTop 10 P2PK non spesi per valore:")
print("-" * 60)
cursor.execute("""
SELECT block_height, txid, output_index, value_satoshi, scriptpubkey
FROM p2pk_addresses
WHERE is_unspent = 1
ORDER BY value_satoshi DESC
LIMIT 10
""")
for i, row in enumerate(cursor.fetchall(), 1):
block, txid, vout, value, script = row
btc = value / 100000000.0
print(f"{i:2d}. Block {block:7d} | {btc:12.8f} BTC | {txid[:16]}...:{vout}")
print("="*60 + "\n")
conn.close()
except sqlite3.Error as e:
print(f"[ERROR] Errore database: {e}")
def main():
# Percorsi di default
db_path = "../databases/bitcoin_p2pk_study.db"
output_file = "target_keys.txt"
print("="*60)
print(" Bitcoin P2PK UTXO Extractor")
print(" Estrae chiavi pubbliche non spese per bruteforce")
print("="*60)
print()
# Controlla argomenti
if len(sys.argv) > 1:
if sys.argv[1] in ['-h', '--help']:
print("Uso:")
print(f" {sys.argv[0]} [database.db] [output.txt]")
print()
print("Esempi:")
print(f" {sys.argv[0]}")
print(f" {sys.argv[0]} custom.db keys.txt")
print(f" {sys.argv[0]} --stats")
return
elif sys.argv[1] == '--stats':
show_stats(db_path)
return
else:
db_path = sys.argv[1]
if len(sys.argv) > 2:
output_file = sys.argv[2]
# Mostra statistiche prima dell'estrazione
show_stats(db_path)
# Conferma utente
print(f"[?] Database: {db_path}")
print(f"[?] Output: {output_file}")
print()
response = input("Procedere con l'estrazione? (s/n): ")
if response.lower() not in ['s', 'y', 'si', 'yes']:
print("[!] Operazione annullata")
return
# Estrai le chiavi
print()
if extract_p2pk_unspent(db_path, output_file):
print()
print("[+] Estrazione completata con successo!")
print(f"[+] Ora puoi eseguire: ./p2pk_bruteforce {output_file}")
else:
print()
print("[!] Estrazione fallita")
sys.exit(1)
if __name__ == "__main__":
main()