diff --git a/LICENSE b/LICENSE index 3be5177..c1f8543 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 bitcoin-playground +Copyright (c) 2026 Davide Grilli Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/__main__.py b/__main__.py index 63c5e7e..1fff581 100644 --- a/__main__.py +++ b/__main__.py @@ -2,8 +2,8 @@ import subprocess import sys def main(): - print("=== GENERATORE INDIRIZZI BITCOIN ===") - print("Seleziona il tipo di indirizzo:") + print("=== BITCOIN ADDRESS GENERATOR ===") + print("Select address type:") print("1. P2PK") print("2. P2PKH") print("3. P2SH") @@ -11,7 +11,7 @@ def main(): print("5. P2TR") print("6. HD Wallet (BIP-44/49/84/86)") - choice = input("Inserisci la tua scelta: ").strip() + choice = input("Enter your choice: ").strip() scripts = { '1': 'src/p2pk.py', @@ -21,16 +21,16 @@ def main(): '5': 'src/p2tr.py', '6': 'src/hd_wallet.py', } - + if choice in scripts: try: subprocess.run([sys.executable, scripts[choice]], check=True) except subprocess.CalledProcessError as e: - print(f"Errore nell'esecuzione dello script: {e}") + print(f"Error running script: {e}") except KeyboardInterrupt: - print("\nOperazione interrotta.") + print("\nOperation interrupted.") else: - print("Scelta non valida.") + print("Invalid choice.") if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/src/hd_wallet.py b/src/hd_wallet.py index 7bb4d26..70fbbe7 100644 --- a/src/hd_wallet.py +++ b/src/hd_wallet.py @@ -56,24 +56,24 @@ def generate_hd_wallet( num_addresses: int = 5, ) -> Dict: """ - Genera un HD wallet deterministico secondo BIP-32/39/44/49/84/86. - Il JSON di output è compatibile con il formato wallet di Electrum. + Generate a deterministic HD wallet following BIP-32/39/44/49/84/86. + The JSON output is compatible with the Electrum wallet format. Args: - network: 'mainnet' o 'testnet' + network: 'mainnet' or 'testnet' bip_type: 'bip44' | 'bip49' | 'bip84' | 'bip86' - mnemonic: frase mnemonica BIP-39 (None = genera nuova a 12 parole) - passphrase: passphrase opzionale BIP-39 - account: indice account (default 0) - num_addresses: quanti indirizzi receiving derivare + mnemonic: BIP-39 mnemonic phrase (None = generate new 12-word mnemonic) + passphrase: optional BIP-39 passphrase + account: account index (default 0) + num_addresses: number of receiving addresses to derive Returns: - Dict compatibile con il formato wallet Electrum. + Dict compatible with Electrum wallet format. """ if network not in COIN_MAP: - raise ValueError(f"Network non supportato: '{network}'. Scegli 'mainnet' o 'testnet'.") + raise ValueError(f"Unsupported network: '{network}'. Choose 'mainnet' or 'testnet'.") if bip_type not in COIN_MAP[network]: - raise ValueError(f"Tipo BIP non supportato: '{bip_type}'. Scegli tra bip44, bip49, bip84, bip86.") + raise ValueError(f"Unsupported BIP type: '{bip_type}'. Choose from bip44, bip49, bip84, bip86.") if mnemonic is None: mnemonic = str(Bip39MnemonicGenerator().FromWordsNumber(Bip39WordsNum.WORDS_NUM_12)) @@ -126,23 +126,23 @@ def main(): print("=== HD Wallet Generator (BIP-32/39/44/49/84/86) ===") network = input("Network (mainnet/testnet): ").strip().lower() - print("Tipo di indirizzo:") - print(" bip44 → P2PKH (legacy, inizia con 1/m)") - print(" bip49 → P2SH-P2WPKH (wrapped SegWit, inizia con 3/2)") - print(" bip84 → P2WPKH (native SegWit, inizia con bc1q/tb1q)") - print(" bip86 → P2TR (Taproot, inizia con bc1p/tb1p)") + print("Address type:") + print(" bip44 → P2PKH (legacy, starts with 1/m)") + print(" bip49 → P2SH-P2WPKH (wrapped SegWit, starts with 3/2)") + print(" bip84 → P2WPKH (native SegWit, starts with bc1q/tb1q)") + print(" bip86 → P2TR (Taproot, starts with bc1p/tb1p)") bip_type = input("BIP type (bip44/bip49/bip84/bip86): ").strip().lower() - mnemonic_input = input("Mnemonic esistente (lascia vuoto per generarne una nuova): ").strip() + mnemonic_input = input("Existing mnemonic (leave blank to generate a new one): ").strip() mnemonic = mnemonic_input if mnemonic_input else None - passphrase = input("Passphrase BIP-39 (lascia vuoto per nessuna): ").strip() + passphrase = input("BIP-39 passphrase (leave blank for none): ").strip() try: account = int(input("Account index (default 0): ").strip() or "0") - num_addresses = int(input("Numero di indirizzi da derivare (default 5): ").strip() or "5") + num_addresses = int(input("Number of addresses to derive (default 5): ").strip() or "5") except ValueError: - print("Valore non valido. Uso i default (account=0, indirizzi=5).") + print("Invalid value. Using defaults (account=0, addresses=5).") account = 0 num_addresses = 5 @@ -157,22 +157,22 @@ def main(): print(f"xpub: {ks['xpub']}") print(f"xprv: {ks['xprv']}") - print(f"\n--- Indirizzi receiving ({num_addresses}) ---") + print(f"\n--- Receiving addresses ({num_addresses}) ---") for a in result['addresses']['receiving']: print(f"[{a['index']}] {a['address']} (path: {a['path']})") print(f" pub: {a['public_key']}") print(f" WIF: {a['private_key_wif']}") - nome = input("\nNome file per salvare (senza estensione, vuoto per saltare): ").strip() - if nome: - if not nome.endswith('.json'): - nome += '.json' - with open(nome, 'w') as f: + filename = input("\nFilename to save (without extension, blank to skip): ").strip() + if filename: + if not filename.endswith('.json'): + filename += '.json' + with open(filename, 'w') as f: json.dump(result, f, indent=4) - print(f"Salvato in {nome}") + print(f"Saved to {filename}") except Exception as e: - print(f"Errore: {e}") + print(f"Error: {e}") if __name__ == '__main__': diff --git a/src/p2pk.py b/src/p2pk.py index 46465b5..c3d5875 100644 --- a/src/p2pk.py +++ b/src/p2pk.py @@ -13,18 +13,18 @@ NETWORK_CONFIG = { def generate_p2pk(network: str = 'mainnet', compressed: bool = False) -> Dict[str, str]: """ - Genera chiave privata, chiave pubblica e WIF per P2PK. + Generate private key, public key and WIF for P2PK. Args: - network: 'mainnet', 'testnet' o 'regtest' - compressed: True per chiave pubblica compressa (33 byte), False per non compressa (65 byte) + network: 'mainnet', 'testnet' or 'regtest' + compressed: True for compressed public key (33 bytes), False for uncompressed (65 bytes) Returns: - Dizionario con network, script_type, private_key_hex, private_key_wif, public_key_hex + Dictionary with network, script_type, private_key_hex, private_key_wif, public_key_hex """ config = NETWORK_CONFIG.get(network) if config is None: - raise ValueError("Network non supportato. Scegli tra 'mainnet', 'testnet' o 'regtest'.") + raise ValueError("Unsupported network. Choose 'mainnet', 'testnet' or 'regtest'.") private_key = secrets.token_bytes(32) private_key_hex = private_key.hex() @@ -60,38 +60,38 @@ def generate_p2pk(network: str = 'mainnet', compressed: bool = False) -> Dict[st } def main(): - """Genera e salva dati P2PK.""" - network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() - compressed_input = input("Utilizzare chiavi compresse (s/n): ").strip().lower() - while compressed_input not in ['s', 'n']: - print("Inserisci 's' per si o 'n' per no.") - compressed_input = input("Utilizzare chiavi compresse (s/n): ").strip().lower() - compressed = (compressed_input == 's') + """Generate and save P2PK data.""" + network = input("Select network (mainnet, testnet, regtest): ").strip().lower() + compressed_input = input("Use compressed keys? (y/n): ").strip().lower() + while compressed_input not in ['y', 'n']: + print("Enter 'y' for yes or 'n' for no.") + compressed_input = input("Use compressed keys? (y/n): ").strip().lower() + compressed = (compressed_input == 'y') try: result = generate_p2pk(network, compressed) - - print("\n--- Risultati ---") + + print("\n--- Results ---") print("Network:", result['network']) print("Script type:", result['script_type']) - print("Chiave privata (hex):", result['private_key_hex']) - print("Chiave privata (WIF):", result['private_key_wif']) - key_type = "compressa" if compressed else "non compressa" - print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) - - nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() - if not nome_file: - nome_file = "dati_p2pk" - print("Nome del file non valido. Verrà utilizzato il nome di default: dati_p2pk.json") - if not nome_file.endswith('.json'): - nome_file += '.json' - - with open(nome_file, 'w') as f: + print("Private key (hex):", result['private_key_hex']) + print("Private key (WIF):", result['private_key_wif']) + key_type = "compressed" if compressed else "uncompressed" + print(f"Public key ({key_type}, hex):", result['public_key_hex']) + + filename = input("\nEnter filename (without extension) to save data: ").strip() + if not filename: + filename = "wallet_p2pk" + print("Invalid filename. Using default: wallet_p2pk.json") + if not filename.endswith('.json'): + filename += '.json' + + with open(filename, 'w') as f: json.dump(result, f, indent=4) - print(f"Dati salvati correttamente nel file: {nome_file}") + print(f"Data saved to: {filename}") except Exception as e: - print("Errore:", e) + print("Error:", e) if __name__ == '__main__': main() diff --git a/src/p2pkh.py b/src/p2pkh.py index dd57867..917cf41 100644 --- a/src/p2pkh.py +++ b/src/p2pkh.py @@ -12,10 +12,10 @@ NETWORK_CONFIG = { } def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]: - """Genera chiave privata, pubblica, WIF e indirizzo Bitcoin Legacy (P2PKH).""" + """Generate private key, public key, WIF and Bitcoin Legacy (P2PKH) address.""" config = NETWORK_CONFIG.get(network) if config is None: - raise ValueError("Network non supportato. Scegli tra 'mainnet', 'testnet' o 'regtest'.") + raise ValueError("Unsupported network. Choose 'mainnet', 'testnet' or 'regtest'.") private_key = secrets.token_bytes(32) private_key_hex = private_key.hex() @@ -23,7 +23,7 @@ def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) - sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1) vk = sk.get_verifying_key() pubkey_bytes = vk.to_string() - + if compressed: x = pubkey_bytes[:32] y = pubkey_bytes[32:] @@ -31,12 +31,12 @@ def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) - pubkey = prefix + x else: pubkey = b'\x04' + pubkey_bytes - + pubkey_hex = pubkey.hex() sha256_pubkey = hashlib.sha256(pubkey).digest() ripemd160 = hashlib.new('ripemd160', sha256_pubkey).digest() - + addr_payload = config['addr_prefix'] + ripemd160 checksum = hashlib.sha256(hashlib.sha256(addr_payload).digest()).digest()[:4] address = base58.b58encode(addr_payload + checksum).decode() @@ -45,7 +45,7 @@ def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) - extended_key = config['wif_prefix'] + private_key + b'\x01' else: extended_key = config['wif_prefix'] + private_key - + checksum = hashlib.sha256(hashlib.sha256(extended_key).digest()).digest()[:4] private_key_wif = base58.b58encode(extended_key + checksum).decode() @@ -59,38 +59,37 @@ def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) - } - def main(): - """Funzione principale che gestisce l'interazione con l'utente e il salvataggio dei dati.""" - network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() - compressed_input = input("Utilizzare chiavi compresse? (s/n): ").strip().lower() + """Main function handling user interaction and data saving.""" + network = input("Select network (mainnet, testnet, regtest): ").strip().lower() + compressed_input = input("Use compressed keys? (y/n): ").strip().lower() compressed = compressed_input != 'n' - + try: result = generate_legacy_address(network, compressed) - - print("\n--- Risultati ---") + + print("\n--- Results ---") print(f"Network: {result['network']}") print(f"Script type: {result['script_type']}") - print("Chiave privata (hex):", result['private_key_hex']) - print("Chiave privata (WIF):", result['private_key_wif']) - key_type = "compressa" if compressed else "non compressa" - print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) - print("Indirizzo:", result['address']) - - nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() - if not nome_file: - nome_file = "wallet" - print("Nome del file non valido. Verrà utilizzato il nome di default: wallet.json") - if not nome_file.endswith('.json'): - nome_file += '.json' - - with open(nome_file, 'w') as f: + print("Private key (hex):", result['private_key_hex']) + print("Private key (WIF):", result['private_key_wif']) + key_type = "compressed" if compressed else "uncompressed" + print(f"Public key ({key_type}, hex):", result['public_key_hex']) + print("Address:", result['address']) + + filename = input("\nEnter filename (without extension) to save data: ").strip() + if not filename: + filename = "wallet" + print("Invalid filename. Using default: wallet.json") + if not filename.endswith('.json'): + filename += '.json' + + with open(filename, 'w') as f: json.dump(result, f, indent=4) - print(f"Dati salvati correttamente nel file: {nome_file}") - + print(f"Data saved to: {filename}") + except Exception as e: - print("Errore:", e) + print("Error:", e) if __name__ == '__main__': main() diff --git a/src/p2sh.py b/src/p2sh.py index ecc79cd..e2df762 100644 --- a/src/p2sh.py +++ b/src/p2sh.py @@ -12,17 +12,17 @@ NETWORK_CONFIG = { } def _to_wif(privkey: bytes, wif_prefix: bytes, compressed: bool = True) -> str: - """Converte una chiave privata in WIF (aggiunge 0x01 se compressa).""" + """Convert a private key to WIF (appends 0x01 if compressed).""" payload = wif_prefix + privkey + (b"\x01" if compressed else b"") checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] return base58.b58encode(payload + checksum).decode() def _gen_keypair(compressed: bool = True): - """Genera (priv_hex, wif, pub_hex).""" + """Generate (priv_hex, wif, pub_hex).""" sk_bytes = secrets.token_bytes(32) sk = ecdsa.SigningKey.from_string(sk_bytes, curve=ecdsa.SECP256k1) vk = sk.get_verifying_key() - raw = vk.to_string() # 64 byte X||Y + raw = vk.to_string() # 64 bytes X||Y if compressed: x = raw[:32] @@ -35,16 +35,16 @@ def _gen_keypair(compressed: bool = True): return sk_bytes.hex(), pub.hex(), pub def _op_push(data: bytes) -> bytes: - """pushdata minimale (lunghezze pubkey/redeem < 0x4c gestite direttamente).""" + """Minimal pushdata (pubkey/redeem lengths < 0x4c handled directly).""" assert len(data) < 0x4c return bytes([len(data)]) + data def _encode_multisig_redeem(m: int, pubkeys: List[bytes], n: int) -> bytes: - """Costruisce redeemScript: OP_m ... OP_n OP_CHECKMULTISIG.""" + """Build redeemScript: OP_m ... OP_n OP_CHECKMULTISIG.""" if not (1 <= m <= n <= 16): - raise ValueError("Richiesto 1 <= m <= n <= 16") + raise ValueError("Required 1 <= m <= n <= 16") if any(len(pk) not in (33, 65) for pk in pubkeys): - raise ValueError("Pubkey non valida (attese compresse 33B o non compresse 65B)") + raise ValueError("Invalid pubkey (expected compressed 33B or uncompressed 65B)") OP_CHECKMULTISIG = b"\xAE" OP_m = bytes([0x50 + m]) # OP_1 .. OP_16 @@ -73,16 +73,16 @@ def generate_p2sh_multisig( m: int = 2, n: int = 3, compressed: bool = True, - sort_pubkeys: bool = True, # BIP67: ordina le pubkey per evitare malleabilità del redeem + sort_pubkeys: bool = True, # BIP67: sort pubkeys to prevent redeem script malleability ) -> Dict: - """Genera JSON per un P2SH multisig m-of-n (con chiavi locali).""" + """Generate JSON for a P2SH m-of-n multisig (with locally generated keys).""" cfg = NETWORK_CONFIG.get(network) if cfg is None: - raise ValueError("Network non supportato (mainnet, testnet, regtest).") + raise ValueError("Unsupported network (mainnet, testnet, regtest).") if not (1 <= m <= n <= 16): - raise ValueError("Parametri m/n non validi (1 <= m <= n <= 16).") + raise ValueError("Invalid m/n parameters (1 <= m <= n <= 16).") - # genera n coppie chiave + # generate n key pairs participants = [] pubkeys_bytes = [] for _ in range(n): @@ -94,7 +94,7 @@ def generate_p2sh_multisig( }) pubkeys_bytes.append(pub_bytes) - # BIP67: ordina le pubkey in modo deterministico (lexicografico sul byte array) + # BIP67: sort pubkeys deterministically (lexicographic on byte array) if sort_pubkeys: pubkeys_bytes.sort() @@ -115,38 +115,38 @@ def generate_p2sh_multisig( return result def _redeem_asm(m: int, pubkeys: List[bytes], n: int) -> str: - """Rappresentazione ASM comoda per debug.""" + """ASM representation for debugging.""" def opnum(x): return f"OP_{x}" items = [opnum(m)] + [pk.hex() for pk in pubkeys] + [opnum(n), "OP_CHECKMULTISIG"] return " ".join(items) def main(): - print("=== Generatore P2SH Multisig ===") - net = input("Seleziona rete (mainnet/testnet/regtest): ").strip().lower() - m = int(input("Quante firme richieste (m)? ").strip()) - n = int(input("Quante chiavi totali (n)? ").strip()) - comp_in = input("Pubkey compresse? (s/n): ").strip().lower() + print("=== P2SH Multisig Generator ===") + net = input("Select network (mainnet/testnet/regtest): ").strip().lower() + m = int(input("How many required signatures (m)? ").strip()) + n = int(input("How many total keys (n)? ").strip()) + comp_in = input("Use compressed pubkeys? (y/n): ").strip().lower() compressed = comp_in != "n" try: res = generate_p2sh_multisig(net, m, n, compressed, sort_pubkeys=True) - print("\n--- Risultati ---") - for k in ["network","script_type","m","n","redeem_script_hex"]: + print("\n--- Results ---") + for k in ["network", "script_type", "m", "n", "redeem_script_hex"]: print(f"{k}: {res[k]}") - print("\n-- Partecipanti --") + print("\n-- Participants --") for i, p in enumerate(res["participants"], 1): print(f"[{i}] pub: {p['public_key_hex']}") print(f" priv: {p['private_key_hex']}") print(f" wif: {p['private_key_wif']}") print(f"\naddress: {res['address']}") - nome = input("\nNome file per salvare (senza estensione): ").strip() or "wallet_p2sh_multisig" - if not nome.endswith(".json"): nome += ".json" - with open(nome, "w") as f: + filename = input("\nFilename to save (without extension): ").strip() or "wallet_p2sh_multisig" + if not filename.endswith(".json"): filename += ".json" + with open(filename, "w") as f: json.dump(res, f, indent=4) - print(f"Salvato in {nome}") + print(f"Saved to {filename}") except Exception as e: - print("Errore:", e) + print("Error:", e) if __name__ == "__main__": main() diff --git a/src/p2tr.py b/src/p2tr.py index bf61aa0..e60fa24 100644 --- a/src/p2tr.py +++ b/src/p2tr.py @@ -12,7 +12,7 @@ NETWORK_CONFIG = { _BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" def _bech32_polymod(values): - """Calcola il polymod per bech32/bech32m""" + """Compute polymod for bech32/bech32m.""" GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] chk = 1 for v in values: @@ -23,23 +23,23 @@ def _bech32_polymod(values): return chk def _bech32_hrp_expand(hrp): - """Espande l'HRP per il calcolo bech32""" + """Expand HRP for bech32 checksum computation.""" return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] def _bech32_create_checksum(hrp, data, spec="bech32m"): - """Crea il checksum per bech32/bech32m""" + """Create checksum for bech32/bech32m.""" const = 0x2bc830a3 if spec == "bech32m" else 1 values = _bech32_hrp_expand(hrp) + data polymod = _bech32_polymod(values + [0,0,0,0,0,0]) ^ const return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] def bech32m_encode(hrp: str, data: list) -> str: - """Codifica dati in formato bech32m""" + """Encode data in bech32m format.""" combined = data + _bech32_create_checksum(hrp, data, "bech32m") return hrp + "1" + "".join([_BECH32_CHARSET[d] for d in combined]) def convertbits(data: bytes, frombits: int, tobits: int, pad: bool = True) -> Optional[list]: - """Converte bit tra diverse basi""" + """Convert bits between different bases.""" acc = 0; bits = 0; ret = [] maxv = (1 << tobits) - 1 max_acc = (1 << (frombits + tobits - 1)) - 1 @@ -56,7 +56,7 @@ def convertbits(data: bytes, frombits: int, tobits: int, pad: bool = True) -> Op return ret def tagged_hash(tag: str, msg: bytes) -> bytes: - """Calcola tagged hash BIP340""" + """Compute BIP340 tagged hash.""" tagh = hashlib.sha256(tag.encode()).digest() return hashlib.sha256(tagh + tagh + msg).digest() @@ -65,33 +65,33 @@ G: Point = curve.generator n = curve.order def point_from_sk(sk_bytes: bytes) -> Point: - """Genera punto pubblico da chiave privata""" + """Derive public key point from private key.""" sk = int.from_bytes(sk_bytes, 'big') - if not (1 <= sk < n): raise ValueError("Chiave privata fuori range") + if not (1 <= sk < n): raise ValueError("Private key out of range") return SigningKey.from_string(sk_bytes, curve=SECP256k1).verifying_key.pubkey.point def xonly_bytes(P: Point) -> bytes: - """Estrae coordinata x da punto (32 byte)""" + """Extract x-coordinate from point (32 bytes).""" return int(P.x()).to_bytes(32, 'big') def pubkey_tweak(P: Point, merkle_root: Optional[bytes] = None): - """Applica tweak Taproot al punto pubblico""" + """Apply Taproot tweak to public key point.""" mr = b"" if merkle_root is None else merkle_root t = int.from_bytes(tagged_hash("TapTweak", xonly_bytes(P) + mr), 'big') % n - if t == 0: raise ValueError("Tweak nullo, rigenera la chiave") + if t == 0: raise ValueError("Zero tweak, regenerate the key") return P + t*G, t def to_wif(privkey: bytes, wif_prefix: bytes) -> str: - """Converte chiave privata in formato WIF""" + """Convert private key to WIF format.""" extended = wif_prefix + privkey + b'\x01' checksum = hashlib.sha256(hashlib.sha256(extended).digest()).digest()[:4] return base58.b58encode(extended + checksum).decode() def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]: - """Genera indirizzo P2TR completo""" + """Generate a full P2TR address.""" cfg = NETWORK_CONFIG.get(network) - if not cfg: raise ValueError("Network non supportato (mainnet, testnet, regtest).") - + if not cfg: raise ValueError("Unsupported network (mainnet, testnet, regtest).") + sk = secrets.token_bytes(32) P = point_from_sk(sk) Q, t = pubkey_tweak(P, merkle_root=None) @@ -99,7 +99,7 @@ def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]: data = [1] + convertbits(prog, 8, 5, True) address = bech32m_encode(cfg['hrp'], data) wif = to_wif(sk, cfg['wif_prefix']) - + return { "network": network, "script_type": "p2tr", @@ -110,17 +110,17 @@ def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]: } def main(): - """Funzione principale interattiva""" - net = input("Seleziona rete (mainnet/testnet/regtest): ").strip().lower() + """Interactive main function.""" + net = input("Select network (mainnet/testnet/regtest): ").strip().lower() try: res = generate_p2tr_address(net) - print("\n--- Risultati P2TR ---") + print("\n--- P2TR Results ---") for k, v in res.items(): print(f"{k}: {v}") - nome = input("\nNome file per salvare (senza estensione): ").strip() or "wallet_p2tr" - if not nome.endswith(".json"): nome += ".json" - with open(nome, "w") as f: json.dump(res, f, indent=4) - print(f"Salvato in {nome}") - except Exception as e: print("Errore:", e) + filename = input("\nFilename to save (without extension): ").strip() or "wallet_p2tr" + if not filename.endswith(".json"): filename += ".json" + with open(filename, "w") as f: json.dump(res, f, indent=4) + print(f"Saved to {filename}") + except Exception as e: print("Error:", e) if __name__ == "__main__": main() diff --git a/src/p2wpkh.py b/src/p2wpkh.py index 98ff2ef..ce632cd 100644 --- a/src/p2wpkh.py +++ b/src/p2wpkh.py @@ -13,10 +13,10 @@ NETWORK_CONFIG = { } def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]: - """Genera chiave privata, pubblica, WIF e indirizzo SegWit bech32.""" + """Generate private key, public key, WIF and SegWit bech32 address.""" config = NETWORK_CONFIG.get(network) if config is None: - raise ValueError("Network non supportato. Scegli tra 'mainnet', 'testnet' o 'regtest'.") + raise ValueError("Unsupported network. Choose 'mainnet', 'testnet' or 'regtest'.") private_key = secrets.token_bytes(32) private_key_hex = private_key.hex() @@ -24,7 +24,7 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) - sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.SECP256k1) vk = sk.get_verifying_key() pubkey_bytes = vk.to_string() - + if compressed: x = pubkey_bytes[:32] y = pubkey_bytes[32:] @@ -32,7 +32,7 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) - pubkey = prefix + x else: pubkey = b'\x04' + pubkey_bytes - + pubkey_hex = pubkey.hex() sha256_pubkey = hashlib.sha256(pubkey).digest() @@ -40,7 +40,7 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) - converted = convertbits(list(ripemd160), 8, 5) if converted is None: - raise ValueError("Errore nella conversione dei bit per la codifica Bech32") + raise ValueError("Error converting bits for Bech32 encoding") data = [0] + converted address = bech32_encode(config['hrp'], data) @@ -48,7 +48,7 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) - extended_key = config['wif_prefix'] + private_key + b'\x01' else: extended_key = config['wif_prefix'] + private_key - + checksum = hashlib.sha256(hashlib.sha256(extended_key).digest()).digest()[:4] private_key_wif = base58.b58encode(extended_key + checksum).decode() @@ -62,36 +62,36 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) - } def main(): - """Funzione principale che gestisce l'interazione con l'utente e il salvataggio dei dati.""" - network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() - compressed_input = input("Utilizzare chiavi compresse? (s/n): ").strip().lower() + """Main function handling user interaction and data saving.""" + network = input("Select network (mainnet, testnet, regtest): ").strip().lower() + compressed_input = input("Use compressed keys? (y/n): ").strip().lower() compressed = compressed_input != 'n' - + try: result = generate_segwit_address(network, compressed) - - print("\n--- Risultati ---") + + print("\n--- Results ---") print(f"Network: {result['network']}") print(f"Script type: {result['script_type']}") - print("Chiave privata (hex):", result['private_key_hex']) - print("Chiave privata (WIF):", result['private_key_wif']) - key_type = "compressa" if compressed else "non compressa" - print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) - print("Indirizzo:", result['address']) - - nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() - if not nome_file: - nome_file = "wallet" - print("Nome del file non valido. Verrà utilizzato il nome di default: wallet.json") - if not nome_file.endswith('.json'): - nome_file += '.json' - - with open(nome_file, 'w') as f: + print("Private key (hex):", result['private_key_hex']) + print("Private key (WIF):", result['private_key_wif']) + key_type = "compressed" if compressed else "uncompressed" + print(f"Public key ({key_type}, hex):", result['public_key_hex']) + print("Address:", result['address']) + + filename = input("\nEnter filename (without extension) to save data: ").strip() + if not filename: + filename = "wallet" + print("Invalid filename. Using default: wallet.json") + if not filename.endswith('.json'): + filename += '.json' + + with open(filename, 'w') as f: json.dump(result, f, indent=4) - print(f"Dati salvati correttamente nel file: {nome_file}") - + print(f"Data saved to: {filename}") + except Exception as e: - print("Errore:", e) + print("Error:", e) if __name__ == '__main__': main()