#!/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: 41ac o 21ac # 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()