#!/usr/bin/env python3 """ Script per visualizzare i dati P2PK dal database Genera un report HTML interattivo """ import sqlite3 import sys from datetime import datetime from pathlib import Path class P2PKDatabaseViewer: def __init__(self, db_path: str = "bitcoin_p2pk_study.db"): self.db_path = db_path def check_database_exists(self) -> bool: """Verifica se il database esiste""" return Path(self.db_path).exists() def get_statistics(self) -> dict: """Ottieni statistiche dal database""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() stats = {} try: # Progresso scansione cursor.execute('SELECT last_scanned_block, total_p2pk_found FROM scan_progress WHERE id = 1') progress = cursor.fetchone() stats['last_block'] = progress[0] if progress else 0 stats['total_found'] = progress[1] if progress else 0 # Totale P2PK nel database cursor.execute('SELECT COUNT(*) FROM p2pk_addresses') stats['total_in_db'] = cursor.fetchone()[0] # Range blocchi cursor.execute('SELECT MIN(block_height), MAX(block_height) FROM p2pk_addresses') min_block, max_block = cursor.fetchone() stats['min_block'] = min_block if min_block is not None else 0 stats['max_block'] = max_block if max_block is not None else 0 # Blocchi unici con P2PK cursor.execute('SELECT COUNT(DISTINCT block_height) FROM p2pk_addresses') stats['unique_blocks'] = cursor.fetchone()[0] # Transazioni unique cursor.execute('SELECT COUNT(DISTINCT txid) FROM p2pk_addresses') stats['unique_txs'] = cursor.fetchone()[0] # P2PK non spesi (con saldo attuale) cursor.execute('SELECT COUNT(*) FROM p2pk_addresses WHERE is_unspent = 1') stats['unspent_count'] = cursor.fetchone()[0] # Valore totale - calcolo manuale per evitare overflow cursor.execute('SELECT value_satoshi FROM p2pk_addresses') all_values = cursor.fetchall() total_sat = 0.0 for (val,) in all_values: if val is not None: total_sat += float(val) stats['total_value_btc'] = total_sat / 100000000.0 stats['total_value_sat'] = int(total_sat) # Valore non speso cursor.execute('SELECT value_satoshi FROM p2pk_addresses WHERE is_unspent = 1') unspent_values = cursor.fetchall() unspent_sat = 0.0 for (val,) in unspent_values: if val is not None: unspent_sat += float(val) stats['unspent_value_btc'] = unspent_sat / 100000000.0 stats['unspent_value_sat'] = int(unspent_sat) # Conta chiavi compresse vs non compresse cursor.execute('SELECT scriptpubkey FROM p2pk_addresses') all_scripts = cursor.fetchall() compressed_count = 0 uncompressed_count = 0 for (script,) in all_scripts: if script and script.startswith('41') and len(script) == 134: uncompressed_count += 1 elif script and script.startswith('21') and len(script) == 70: compressed_count += 1 stats['compressed_count'] = compressed_count stats['uncompressed_count'] = uncompressed_count except Exception as e: print(f"โš ๏ธ Errore nel calcolo statistiche: {e}") import traceback traceback.print_exc() stats = { 'last_block': 0, 'total_found': 0, 'total_in_db': 0, 'min_block': 0, 'max_block': 0, 'unique_blocks': 0, 'total_value_btc': 0.0, 'total_value_sat': 0, 'unique_txs': 0, 'unspent_count': 0, 'unspent_value_btc': 0.0, 'unspent_value_sat': 0, 'compressed_count': 0, 'uncompressed_count': 0 } conn.close() return stats def get_all_p2pk(self, limit: int = None) -> list: """Ottieni tutti i P2PK dal database""" conn = sqlite3.connect(self.db_path) cursor = conn.cursor() query = ''' SELECT id, block_height, txid, output_index, scriptpubkey, value_satoshi, timestamp, is_unspent, last_checked FROM p2pk_addresses ORDER BY block_height ASC, id ASC ''' if limit: query += f' LIMIT {limit}' cursor.execute(query) results = cursor.fetchall() conn.close() return results def extract_pubkey_from_script(self, scriptpubkey: str) -> str: """Estrae la chiave pubblica dallo scriptPubKey""" if not scriptpubkey: return "" # P2PK non compresso: 41 + pubkey (130 hex chars) + ac if scriptpubkey.startswith('41') and len(scriptpubkey) == 134: return scriptpubkey[2:132] # Rimuovi 41 all'inizio e ac alla fine # P2PK compresso: 21 + pubkey (66 hex chars) + ac elif scriptpubkey.startswith('21') and len(scriptpubkey) == 70: return scriptpubkey[2:68] # Rimuovi 21 all'inizio e ac alla fine return scriptpubkey def _generate_table_html(self, p2pk_data: list) -> str: """Genera l'HTML della tabella""" if not p2pk_data: return '
๐Ÿ“ญ Nessun dato P2PK trovato nel database
' rows_html = [] for row in p2pk_data: scriptpubkey = row[4] pubkey = self.extract_pubkey_from_script(scriptpubkey) txid_short = row[2][:16] if len(row[2]) > 16 else row[2] timestamp_str = datetime.fromtimestamp(row[6]).strftime('%Y-%m-%d %H:%M') if row[6] else 'N/A' # row[5] รจ giร  in satoshi, lo convertiamo in BTC dividendo per 100000000 value_satoshi = row[5] value_btc = value_satoshi / 100000000.0 # Stato UTXO (row[7] = is_unspent) is_unspent = row[7] if len(row) > 7 else 0 utxo_status = '๐ŸŸข NON SPESO' if is_unspent else '๐Ÿ”ด SPESO' utxo_class = 'unspent' if is_unspent else 'spent' # Determina se la chiave รจ compressa o non compressa if scriptpubkey and scriptpubkey.startswith('41') and len(scriptpubkey) == 134: key_type = 'uncompressed' key_type_badge = '๐Ÿ“œ Non Compressa (65 bytes)' elif scriptpubkey and scriptpubkey.startswith('21') and len(scriptpubkey) == 70: key_type = 'compressed' key_type_badge = '๐Ÿ“ฆ Compressa (33 bytes)' else: key_type = 'unknown' key_type_badge = 'โ“ Sconosciuta' row_html = f''' {row[0]} {row[1]} {txid_short}... {row[3]}
{key_type_badge}
{pubkey}
{value_btc:.8f} BTC
({value_satoshi:,} sat) {utxo_status} {timestamp_str} ''' rows_html.append(row_html) return f''' {''.join(rows_html)}
ID Blocco TXID Output Chiave Pubblica Valore (BTC) Stato UTXO Timestamp
''' def generate_html_report(self, output_file: str = "p2pk_report.html"): """Genera un report HTML interattivo""" if not self.check_database_exists(): print(f"โŒ Database non trovato: {self.db_path}") return stats = self.get_statistics() p2pk_data = self.get_all_p2pk() html = f""" P2PK Database Report

๐Ÿ” P2PK Database Report

Bitcoin Pay-to-Public-Key Transaction Scanner

Database: {self.db_path}

Ultimo Blocco
{stats['last_block']:,}
P2PK Trovati
{stats['total_in_db']:,}
Blocchi Unici
{stats['unique_blocks']:,}
Valore Totale
{stats['total_value_btc']:.8f} BTC
Range Blocchi
{stats['min_block']:,} - {stats['max_block']:,}
Transazioni Uniche
{stats['unique_txs']:,}
๐Ÿ’ฐ P2PK Non Spesi
{stats['unspent_count']:,}
๐Ÿ’Ž Valore Non Speso
{stats['unspent_value_btc']:.8f} BTC
๐Ÿ“œ Chiavi Non Compresse
{stats['uncompressed_count']:,}
๐Ÿ“ฆ Chiavi Compresse
{stats['compressed_count']:,}
{self._generate_table_html(p2pk_data)}
""" with open(output_file, 'w', encoding='utf-8') as f: f.write(html) print(f"โœ… Report HTML generato: {output_file}") print(f"๐Ÿ“Š Statistiche:") print(f" - P2PK totali: {stats['total_in_db']}") print(f" - Blocchi scansionati: {stats['last_block']}") print(f" - Valore totale: {stats['total_value_btc']:.8f} BTC") def print_console_report(self): """Stampa un report nel terminale""" if not self.check_database_exists(): print(f"โŒ Database non trovato: {self.db_path}") return stats = self.get_statistics() print("\n" + "="*60) print("๐Ÿ“Š REPORT DATABASE P2PK") print("="*60) print(f"๐Ÿ“ Database: {self.db_path}") print(f"๐Ÿ“ฆ Ultimo blocco scansionato: {stats['last_block']:,}") print(f"๐Ÿ”‘ P2PK totali trovati: {stats['total_in_db']:,}") print(f"๐Ÿ“Š Blocchi unici con P2PK: {stats['unique_blocks']:,}") print(f"๐Ÿ“ˆ Range blocchi: {stats['min_block']:,} - {stats['max_block']:,}") print(f"๐Ÿ’ฐ Valore totale: {stats['total_value_btc']:.8f} BTC ({stats['total_value_sat']:,} sat)") print(f"๐Ÿ“ Transazioni uniche: {stats['unique_txs']:,}") print("-"*60) print(f"๐Ÿ’Ž P2PK NON SPESI: {stats['unspent_count']:,}") print(f"๐Ÿ’ต Valore non speso: {stats['unspent_value_btc']:.8f} BTC ({stats['unspent_value_sat']:,} sat)") print("="*60) # Mostra alcuni esempi p2pk_data = self.get_all_p2pk(limit=5) if p2pk_data: print("\n๐Ÿ” Primi 5 P2PK trovati:") for row in p2pk_data: pubkey = self.extract_pubkey_from_script(row[4]) print(f"\n Blocco {row[1]} | TX: {row[2][:16]}...") print(f" Pubkey: {pubkey[:40]}...") print(f" Valore: {row[5] / 100000000:.8f} BTC") if __name__ == "__main__": db_file = "bitcoin_p2pk_study.db" # Se viene passato un argomento, usalo come path del database if len(sys.argv) > 1: db_file = sys.argv[1] viewer = P2PKDatabaseViewer(db_file) print("๐Ÿ” P2PK Database Viewer") print("="*60) print("Scegli un'opzione:") print("1. Genera report HTML") print("2. Mostra report nel terminale") print("3. Entrambi") print("="*60) choice = input("Scelta (1/2/3): ").strip() if choice == "1": viewer.generate_html_report() elif choice == "2": viewer.print_console_report() elif choice == "3": viewer.generate_html_report() viewer.print_console_report() else: print("โŒ Scelta non valida")