243 lines
7.6 KiB
Python
Executable File
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()
|