chore: translate all source files to English

- Translate docstrings, comments, print statements, input prompts
  and error messages across all src/ scripts and __main__.py
- Update LICENSE copyright holder to Davide Grilli (2026)
- p2pk: input prompt changed from s/n to y/n for compressed key selection
This commit is contained in:
2026-03-09 12:14:11 +01:00
parent 2727844ec8
commit 040a72c378
8 changed files with 174 additions and 175 deletions

View File

@@ -1,6 +1,6 @@
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -2,8 +2,8 @@ import subprocess
import sys import sys
def main(): def main():
print("=== GENERATORE INDIRIZZI BITCOIN ===") print("=== BITCOIN ADDRESS GENERATOR ===")
print("Seleziona il tipo di indirizzo:") print("Select address type:")
print("1. P2PK") print("1. P2PK")
print("2. P2PKH") print("2. P2PKH")
print("3. P2SH") print("3. P2SH")
@@ -11,7 +11,7 @@ def main():
print("5. P2TR") print("5. P2TR")
print("6. HD Wallet (BIP-44/49/84/86)") print("6. HD Wallet (BIP-44/49/84/86)")
choice = input("Inserisci la tua scelta: ").strip() choice = input("Enter your choice: ").strip()
scripts = { scripts = {
'1': 'src/p2pk.py', '1': 'src/p2pk.py',
@@ -26,11 +26,11 @@ def main():
try: try:
subprocess.run([sys.executable, scripts[choice]], check=True) subprocess.run([sys.executable, scripts[choice]], check=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f"Errore nell'esecuzione dello script: {e}") print(f"Error running script: {e}")
except KeyboardInterrupt: except KeyboardInterrupt:
print("\nOperazione interrotta.") print("\nOperation interrupted.")
else: else:
print("Scelta non valida.") print("Invalid choice.")
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -56,24 +56,24 @@ def generate_hd_wallet(
num_addresses: int = 5, num_addresses: int = 5,
) -> Dict: ) -> Dict:
""" """
Genera un HD wallet deterministico secondo BIP-32/39/44/49/84/86. Generate a deterministic HD wallet following BIP-32/39/44/49/84/86.
Il JSON di output è compatibile con il formato wallet di Electrum. The JSON output is compatible with the Electrum wallet format.
Args: Args:
network: 'mainnet' o 'testnet' network: 'mainnet' or 'testnet'
bip_type: 'bip44' | 'bip49' | 'bip84' | 'bip86' bip_type: 'bip44' | 'bip49' | 'bip84' | 'bip86'
mnemonic: frase mnemonica BIP-39 (None = genera nuova a 12 parole) mnemonic: BIP-39 mnemonic phrase (None = generate new 12-word mnemonic)
passphrase: passphrase opzionale BIP-39 passphrase: optional BIP-39 passphrase
account: indice account (default 0) account: account index (default 0)
num_addresses: quanti indirizzi receiving derivare num_addresses: number of receiving addresses to derive
Returns: Returns:
Dict compatibile con il formato wallet Electrum. Dict compatible with Electrum wallet format.
""" """
if network not in COIN_MAP: 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]: 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: if mnemonic is None:
mnemonic = str(Bip39MnemonicGenerator().FromWordsNumber(Bip39WordsNum.WORDS_NUM_12)) 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) ===") print("=== HD Wallet Generator (BIP-32/39/44/49/84/86) ===")
network = input("Network (mainnet/testnet): ").strip().lower() network = input("Network (mainnet/testnet): ").strip().lower()
print("Tipo di indirizzo:") print("Address type:")
print(" bip44 → P2PKH (legacy, inizia con 1/m)") print(" bip44 → P2PKH (legacy, starts with 1/m)")
print(" bip49 → P2SH-P2WPKH (wrapped SegWit, inizia con 3/2)") print(" bip49 → P2SH-P2WPKH (wrapped SegWit, starts with 3/2)")
print(" bip84 → P2WPKH (native SegWit, inizia con bc1q/tb1q)") print(" bip84 → P2WPKH (native SegWit, starts with bc1q/tb1q)")
print(" bip86 → P2TR (Taproot, inizia con bc1p/tb1p)") print(" bip86 → P2TR (Taproot, starts with bc1p/tb1p)")
bip_type = input("BIP type (bip44/bip49/bip84/bip86): ").strip().lower() 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 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: try:
account = int(input("Account index (default 0): ").strip() or "0") 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: except ValueError:
print("Valore non valido. Uso i default (account=0, indirizzi=5).") print("Invalid value. Using defaults (account=0, addresses=5).")
account = 0 account = 0
num_addresses = 5 num_addresses = 5
@@ -157,22 +157,22 @@ def main():
print(f"xpub: {ks['xpub']}") print(f"xpub: {ks['xpub']}")
print(f"xprv: {ks['xprv']}") 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']: for a in result['addresses']['receiving']:
print(f"[{a['index']}] {a['address']} (path: {a['path']})") print(f"[{a['index']}] {a['address']} (path: {a['path']})")
print(f" pub: {a['public_key']}") print(f" pub: {a['public_key']}")
print(f" WIF: {a['private_key_wif']}") print(f" WIF: {a['private_key_wif']}")
nome = input("\nNome file per salvare (senza estensione, vuoto per saltare): ").strip() filename = input("\nFilename to save (without extension, blank to skip): ").strip()
if nome: if filename:
if not nome.endswith('.json'): if not filename.endswith('.json'):
nome += '.json' filename += '.json'
with open(nome, 'w') as f: with open(filename, 'w') as f:
json.dump(result, f, indent=4) json.dump(result, f, indent=4)
print(f"Salvato in {nome}") print(f"Saved to {filename}")
except Exception as e: except Exception as e:
print(f"Errore: {e}") print(f"Error: {e}")
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -13,18 +13,18 @@ NETWORK_CONFIG = {
def generate_p2pk(network: str = 'mainnet', compressed: bool = False) -> Dict[str, str]: 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: Args:
network: 'mainnet', 'testnet' o 'regtest' network: 'mainnet', 'testnet' or 'regtest'
compressed: True per chiave pubblica compressa (33 byte), False per non compressa (65 byte) compressed: True for compressed public key (33 bytes), False for uncompressed (65 bytes)
Returns: 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) config = NETWORK_CONFIG.get(network)
if config is None: 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 = secrets.token_bytes(32)
private_key_hex = private_key.hex() private_key_hex = private_key.hex()
@@ -60,38 +60,38 @@ def generate_p2pk(network: str = 'mainnet', compressed: bool = False) -> Dict[st
} }
def main(): def main():
"""Genera e salva dati P2PK.""" """Generate and save P2PK data."""
network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() network = input("Select network (mainnet, testnet, regtest): ").strip().lower()
compressed_input = input("Utilizzare chiavi compresse (s/n): ").strip().lower() compressed_input = input("Use compressed keys? (y/n): ").strip().lower()
while compressed_input not in ['s', 'n']: while compressed_input not in ['y', 'n']:
print("Inserisci 's' per si o 'n' per no.") print("Enter 'y' for yes or 'n' for no.")
compressed_input = input("Utilizzare chiavi compresse (s/n): ").strip().lower() compressed_input = input("Use compressed keys? (y/n): ").strip().lower()
compressed = (compressed_input == 's') compressed = (compressed_input == 'y')
try: try:
result = generate_p2pk(network, compressed) result = generate_p2pk(network, compressed)
print("\n--- Risultati ---") print("\n--- Results ---")
print("Network:", result['network']) print("Network:", result['network'])
print("Script type:", result['script_type']) print("Script type:", result['script_type'])
print("Chiave privata (hex):", result['private_key_hex']) print("Private key (hex):", result['private_key_hex'])
print("Chiave privata (WIF):", result['private_key_wif']) print("Private key (WIF):", result['private_key_wif'])
key_type = "compressa" if compressed else "non compressa" key_type = "compressed" if compressed else "uncompressed"
print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) print(f"Public key ({key_type}, hex):", result['public_key_hex'])
nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() filename = input("\nEnter filename (without extension) to save data: ").strip()
if not nome_file: if not filename:
nome_file = "dati_p2pk" filename = "wallet_p2pk"
print("Nome del file non valido. Verrà utilizzato il nome di default: dati_p2pk.json") print("Invalid filename. Using default: wallet_p2pk.json")
if not nome_file.endswith('.json'): if not filename.endswith('.json'):
nome_file += '.json' filename += '.json'
with open(nome_file, 'w') as f: with open(filename, 'w') as f:
json.dump(result, f, indent=4) 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: except Exception as e:
print("Errore:", e) print("Error:", e)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -12,10 +12,10 @@ NETWORK_CONFIG = {
} }
def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]: 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) config = NETWORK_CONFIG.get(network)
if config is None: 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 = secrets.token_bytes(32)
private_key_hex = private_key.hex() private_key_hex = private_key.hex()
@@ -59,38 +59,37 @@ def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) -
} }
def main(): def main():
"""Funzione principale che gestisce l'interazione con l'utente e il salvataggio dei dati.""" """Main function handling user interaction and data saving."""
network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() network = input("Select network (mainnet, testnet, regtest): ").strip().lower()
compressed_input = input("Utilizzare chiavi compresse? (s/n): ").strip().lower() compressed_input = input("Use compressed keys? (y/n): ").strip().lower()
compressed = compressed_input != 'n' compressed = compressed_input != 'n'
try: try:
result = generate_legacy_address(network, compressed) result = generate_legacy_address(network, compressed)
print("\n--- Risultati ---") print("\n--- Results ---")
print(f"Network: {result['network']}") print(f"Network: {result['network']}")
print(f"Script type: {result['script_type']}") print(f"Script type: {result['script_type']}")
print("Chiave privata (hex):", result['private_key_hex']) print("Private key (hex):", result['private_key_hex'])
print("Chiave privata (WIF):", result['private_key_wif']) print("Private key (WIF):", result['private_key_wif'])
key_type = "compressa" if compressed else "non compressa" key_type = "compressed" if compressed else "uncompressed"
print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) print(f"Public key ({key_type}, hex):", result['public_key_hex'])
print("Indirizzo:", result['address']) print("Address:", result['address'])
nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() filename = input("\nEnter filename (without extension) to save data: ").strip()
if not nome_file: if not filename:
nome_file = "wallet" filename = "wallet"
print("Nome del file non valido. Verrà utilizzato il nome di default: wallet.json") print("Invalid filename. Using default: wallet.json")
if not nome_file.endswith('.json'): if not filename.endswith('.json'):
nome_file += '.json' filename += '.json'
with open(nome_file, 'w') as f: with open(filename, 'w') as f:
json.dump(result, f, indent=4) 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: except Exception as e:
print("Errore:", e) print("Error:", e)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@@ -12,17 +12,17 @@ NETWORK_CONFIG = {
} }
def _to_wif(privkey: bytes, wif_prefix: bytes, compressed: bool = True) -> str: 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"") payload = wif_prefix + privkey + (b"\x01" if compressed else b"")
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4] checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
return base58.b58encode(payload + checksum).decode() return base58.b58encode(payload + checksum).decode()
def _gen_keypair(compressed: bool = True): 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_bytes = secrets.token_bytes(32)
sk = ecdsa.SigningKey.from_string(sk_bytes, curve=ecdsa.SECP256k1) sk = ecdsa.SigningKey.from_string(sk_bytes, curve=ecdsa.SECP256k1)
vk = sk.get_verifying_key() vk = sk.get_verifying_key()
raw = vk.to_string() # 64 byte X||Y raw = vk.to_string() # 64 bytes X||Y
if compressed: if compressed:
x = raw[:32] x = raw[:32]
@@ -35,16 +35,16 @@ def _gen_keypair(compressed: bool = True):
return sk_bytes.hex(), pub.hex(), pub return sk_bytes.hex(), pub.hex(), pub
def _op_push(data: bytes) -> bytes: 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 assert len(data) < 0x4c
return bytes([len(data)]) + data return bytes([len(data)]) + data
def _encode_multisig_redeem(m: int, pubkeys: List[bytes], n: int) -> bytes: def _encode_multisig_redeem(m: int, pubkeys: List[bytes], n: int) -> bytes:
"""Costruisce redeemScript: OP_m <pub1> ... <pubN> OP_n OP_CHECKMULTISIG.""" """Build redeemScript: OP_m <pub1> ... <pubN> OP_n OP_CHECKMULTISIG."""
if not (1 <= m <= n <= 16): 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): 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_CHECKMULTISIG = b"\xAE"
OP_m = bytes([0x50 + m]) # OP_1 .. OP_16 OP_m = bytes([0x50 + m]) # OP_1 .. OP_16
@@ -73,16 +73,16 @@ def generate_p2sh_multisig(
m: int = 2, m: int = 2,
n: int = 3, n: int = 3,
compressed: bool = True, 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: ) -> 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) cfg = NETWORK_CONFIG.get(network)
if cfg is None: 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): 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 = [] participants = []
pubkeys_bytes = [] pubkeys_bytes = []
for _ in range(n): for _ in range(n):
@@ -94,7 +94,7 @@ def generate_p2sh_multisig(
}) })
pubkeys_bytes.append(pub_bytes) 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: if sort_pubkeys:
pubkeys_bytes.sort() pubkeys_bytes.sort()
@@ -115,38 +115,38 @@ def generate_p2sh_multisig(
return result return result
def _redeem_asm(m: int, pubkeys: List[bytes], n: int) -> str: 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}" def opnum(x): return f"OP_{x}"
items = [opnum(m)] + [pk.hex() for pk in pubkeys] + [opnum(n), "OP_CHECKMULTISIG"] items = [opnum(m)] + [pk.hex() for pk in pubkeys] + [opnum(n), "OP_CHECKMULTISIG"]
return " ".join(items) return " ".join(items)
def main(): def main():
print("=== Generatore P2SH Multisig ===") print("=== P2SH Multisig Generator ===")
net = input("Seleziona rete (mainnet/testnet/regtest): ").strip().lower() net = input("Select network (mainnet/testnet/regtest): ").strip().lower()
m = int(input("Quante firme richieste (m)? ").strip()) m = int(input("How many required signatures (m)? ").strip())
n = int(input("Quante chiavi totali (n)? ").strip()) n = int(input("How many total keys (n)? ").strip())
comp_in = input("Pubkey compresse? (s/n): ").strip().lower() comp_in = input("Use compressed pubkeys? (y/n): ").strip().lower()
compressed = comp_in != "n" compressed = comp_in != "n"
try: try:
res = generate_p2sh_multisig(net, m, n, compressed, sort_pubkeys=True) res = generate_p2sh_multisig(net, m, n, compressed, sort_pubkeys=True)
print("\n--- Risultati ---") print("\n--- Results ---")
for k in ["network", "script_type", "m", "n", "redeem_script_hex"]: for k in ["network", "script_type", "m", "n", "redeem_script_hex"]:
print(f"{k}: {res[k]}") print(f"{k}: {res[k]}")
print("\n-- Partecipanti --") print("\n-- Participants --")
for i, p in enumerate(res["participants"], 1): for i, p in enumerate(res["participants"], 1):
print(f"[{i}] pub: {p['public_key_hex']}") print(f"[{i}] pub: {p['public_key_hex']}")
print(f" priv: {p['private_key_hex']}") print(f" priv: {p['private_key_hex']}")
print(f" wif: {p['private_key_wif']}") print(f" wif: {p['private_key_wif']}")
print(f"\naddress: {res['address']}") print(f"\naddress: {res['address']}")
nome = input("\nNome file per salvare (senza estensione): ").strip() or "wallet_p2sh_multisig" filename = input("\nFilename to save (without extension): ").strip() or "wallet_p2sh_multisig"
if not nome.endswith(".json"): nome += ".json" if not filename.endswith(".json"): filename += ".json"
with open(nome, "w") as f: with open(filename, "w") as f:
json.dump(res, f, indent=4) json.dump(res, f, indent=4)
print(f"Salvato in {nome}") print(f"Saved to {filename}")
except Exception as e: except Exception as e:
print("Errore:", e) print("Error:", e)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -12,7 +12,7 @@ NETWORK_CONFIG = {
_BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" _BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def _bech32_polymod(values): def _bech32_polymod(values):
"""Calcola il polymod per bech32/bech32m""" """Compute polymod for bech32/bech32m."""
GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1 chk = 1
for v in values: for v in values:
@@ -23,23 +23,23 @@ def _bech32_polymod(values):
return chk return chk
def _bech32_hrp_expand(hrp): 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] return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
def _bech32_create_checksum(hrp, data, spec="bech32m"): 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 const = 0x2bc830a3 if spec == "bech32m" else 1
values = _bech32_hrp_expand(hrp) + data values = _bech32_hrp_expand(hrp) + data
polymod = _bech32_polymod(values + [0,0,0,0,0,0]) ^ const polymod = _bech32_polymod(values + [0,0,0,0,0,0]) ^ const
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
def bech32m_encode(hrp: str, data: list) -> str: 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") combined = data + _bech32_create_checksum(hrp, data, "bech32m")
return hrp + "1" + "".join([_BECH32_CHARSET[d] for d in combined]) return hrp + "1" + "".join([_BECH32_CHARSET[d] for d in combined])
def convertbits(data: bytes, frombits: int, tobits: int, pad: bool = True) -> Optional[list]: 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 = [] acc = 0; bits = 0; ret = []
maxv = (1 << tobits) - 1 maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 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 return ret
def tagged_hash(tag: str, msg: bytes) -> bytes: def tagged_hash(tag: str, msg: bytes) -> bytes:
"""Calcola tagged hash BIP340""" """Compute BIP340 tagged hash."""
tagh = hashlib.sha256(tag.encode()).digest() tagh = hashlib.sha256(tag.encode()).digest()
return hashlib.sha256(tagh + tagh + msg).digest() return hashlib.sha256(tagh + tagh + msg).digest()
@@ -65,32 +65,32 @@ G: Point = curve.generator
n = curve.order n = curve.order
def point_from_sk(sk_bytes: bytes) -> Point: 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') 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 return SigningKey.from_string(sk_bytes, curve=SECP256k1).verifying_key.pubkey.point
def xonly_bytes(P: Point) -> bytes: 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') return int(P.x()).to_bytes(32, 'big')
def pubkey_tweak(P: Point, merkle_root: Optional[bytes] = None): 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 mr = b"" if merkle_root is None else merkle_root
t = int.from_bytes(tagged_hash("TapTweak", xonly_bytes(P) + mr), 'big') % n 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 return P + t*G, t
def to_wif(privkey: bytes, wif_prefix: bytes) -> str: 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' extended = wif_prefix + privkey + b'\x01'
checksum = hashlib.sha256(hashlib.sha256(extended).digest()).digest()[:4] checksum = hashlib.sha256(hashlib.sha256(extended).digest()).digest()[:4]
return base58.b58encode(extended + checksum).decode() return base58.b58encode(extended + checksum).decode()
def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]: def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]:
"""Genera indirizzo P2TR completo""" """Generate a full P2TR address."""
cfg = NETWORK_CONFIG.get(network) 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) sk = secrets.token_bytes(32)
P = point_from_sk(sk) P = point_from_sk(sk)
@@ -110,17 +110,17 @@ def generate_p2tr_address(network: str = 'mainnet') -> Dict[str, str]:
} }
def main(): def main():
"""Funzione principale interattiva""" """Interactive main function."""
net = input("Seleziona rete (mainnet/testnet/regtest): ").strip().lower() net = input("Select network (mainnet/testnet/regtest): ").strip().lower()
try: try:
res = generate_p2tr_address(net) res = generate_p2tr_address(net)
print("\n--- Risultati P2TR ---") print("\n--- P2TR Results ---")
for k, v in res.items(): print(f"{k}: {v}") for k, v in res.items(): print(f"{k}: {v}")
nome = input("\nNome file per salvare (senza estensione): ").strip() or "wallet_p2tr" filename = input("\nFilename to save (without extension): ").strip() or "wallet_p2tr"
if not nome.endswith(".json"): nome += ".json" if not filename.endswith(".json"): filename += ".json"
with open(nome, "w") as f: json.dump(res, f, indent=4) 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) except Exception as e: print("Error:", e)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -13,10 +13,10 @@ NETWORK_CONFIG = {
} }
def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]: 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) config = NETWORK_CONFIG.get(network)
if config is None: 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 = secrets.token_bytes(32)
private_key_hex = private_key.hex() private_key_hex = private_key.hex()
@@ -40,7 +40,7 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) -
converted = convertbits(list(ripemd160), 8, 5) converted = convertbits(list(ripemd160), 8, 5)
if converted is None: 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 data = [0] + converted
address = bech32_encode(config['hrp'], data) address = bech32_encode(config['hrp'], data)
@@ -62,36 +62,36 @@ def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) -
} }
def main(): def main():
"""Funzione principale che gestisce l'interazione con l'utente e il salvataggio dei dati.""" """Main function handling user interaction and data saving."""
network = input("Seleziona il tipo di rete (mainnet, testnet, regtest): ").strip().lower() network = input("Select network (mainnet, testnet, regtest): ").strip().lower()
compressed_input = input("Utilizzare chiavi compresse? (s/n): ").strip().lower() compressed_input = input("Use compressed keys? (y/n): ").strip().lower()
compressed = compressed_input != 'n' compressed = compressed_input != 'n'
try: try:
result = generate_segwit_address(network, compressed) result = generate_segwit_address(network, compressed)
print("\n--- Risultati ---") print("\n--- Results ---")
print(f"Network: {result['network']}") print(f"Network: {result['network']}")
print(f"Script type: {result['script_type']}") print(f"Script type: {result['script_type']}")
print("Chiave privata (hex):", result['private_key_hex']) print("Private key (hex):", result['private_key_hex'])
print("Chiave privata (WIF):", result['private_key_wif']) print("Private key (WIF):", result['private_key_wif'])
key_type = "compressa" if compressed else "non compressa" key_type = "compressed" if compressed else "uncompressed"
print(f"Chiave pubblica ({key_type}, hex):", result['public_key_hex']) print(f"Public key ({key_type}, hex):", result['public_key_hex'])
print("Indirizzo:", result['address']) print("Address:", result['address'])
nome_file = input("\nInserisci il nome del file (senza estensione) per salvare i dati: ").strip() filename = input("\nEnter filename (without extension) to save data: ").strip()
if not nome_file: if not filename:
nome_file = "wallet" filename = "wallet"
print("Nome del file non valido. Verrà utilizzato il nome di default: wallet.json") print("Invalid filename. Using default: wallet.json")
if not nome_file.endswith('.json'): if not filename.endswith('.json'):
nome_file += '.json' filename += '.json'
with open(nome_file, 'w') as f: with open(filename, 'w') as f:
json.dump(result, f, indent=4) 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: except Exception as e:
print("Errore:", e) print("Error:", e)
if __name__ == '__main__': if __name__ == '__main__':
main() main()