Imposta app desktop Electron/Vue con backend FastAPI

- Aggiunge scaffold frontend Vue con UI base e build verso renderer
- Introduce backend FastAPI con endpoint generazione e salvataggio JSON
- Sposta i generatori in backend e aggiorna CLI main.py
- Aggiorna README, requirements e .gitignore per artefatti e config
- Configura Electron (main/preload) e script npm per dev/build
This commit is contained in:
2026-01-30 16:22:09 +01:00
parent cc34cf23df
commit 5477555429
20 changed files with 5758 additions and 20 deletions

0
backend/__init__.py Normal file
View File

177
backend/app.py Normal file
View File

@@ -0,0 +1,177 @@
import json
import os
import sys
from datetime import datetime
from typing import Optional
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if ROOT_DIR not in sys.path:
sys.path.insert(0, ROOT_DIR)
import p2pk
import p2pkh
import p2sh
import p2tr
import p2wpkh
app = FastAPI(title="AddressGen API", version="0.1.0")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
class P2PKRequest(BaseModel):
network: str = "mainnet"
compressed: bool = False
save_to_file: bool = False
filename: Optional[str] = None
class P2PKHRequest(BaseModel):
network: str = "mainnet"
compressed: bool = True
save_to_file: bool = False
filename: Optional[str] = None
class P2WPKHRequest(BaseModel):
network: str = "mainnet"
compressed: bool = True
save_to_file: bool = False
filename: Optional[str] = None
class P2TRRequest(BaseModel):
network: str = "mainnet"
save_to_file: bool = False
filename: Optional[str] = None
class P2SHRequest(BaseModel):
network: str = "mainnet"
m: int = 2
n: int = 3
compressed: bool = True
sort_pubkeys: bool = True
save_to_file: bool = False
filename: Optional[str] = None
class SaveRequest(BaseModel):
data: dict
filename: Optional[str] = None
def _save_json(data: dict, filename: Optional[str] = None) -> str:
wallets_dir = os.path.join(ROOT_DIR, "wallets")
os.makedirs(wallets_dir, exist_ok=True)
if filename:
safe = os.path.basename(filename)
if not safe.endswith(".json"):
safe += ".json"
out_name = safe
else:
script_type = data.get("script_type", "wallet")
network = data.get("network", "net")
stamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
out_name = f"{script_type}_{network}_{stamp}.json"
out_path = os.path.join(wallets_dir, out_name)
with open(out_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4)
return out_path
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/p2pk")
def create_p2pk(req: P2PKRequest):
try:
result = p2pk.generate_p2pk(req.network, req.compressed)
if req.save_to_file:
result["saved_path"] = _save_json(result, req.filename)
return result
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@app.post("/p2pkh")
def create_p2pkh(req: P2PKHRequest):
try:
result = p2pkh.generate_legacy_address(req.network, req.compressed)
if req.save_to_file:
result["saved_path"] = _save_json(result, req.filename)
return result
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@app.post("/p2wpkh")
def create_p2wpkh(req: P2WPKHRequest):
try:
result = p2wpkh.generate_segwit_address(req.network, req.compressed)
if req.save_to_file:
result["saved_path"] = _save_json(result, req.filename)
return result
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@app.post("/p2tr")
def create_p2tr(req: P2TRRequest):
try:
result = p2tr.generate_p2tr_address(req.network)
if req.save_to_file:
result["saved_path"] = _save_json(result, req.filename)
return result
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@app.post("/p2sh")
def create_p2sh(req: P2SHRequest):
try:
result = p2sh.generate_p2sh_multisig(
network=req.network,
m=req.m,
n=req.n,
compressed=req.compressed,
sort_pubkeys=req.sort_pubkeys,
)
if req.save_to_file:
result["saved_path"] = _save_json(result, req.filename)
return result
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
@app.post("/save")
def save_result(req: SaveRequest):
try:
saved_path = _save_json(req.data, req.filename)
return {"saved_path": saved_path}
except Exception as exc:
raise HTTPException(status_code=400, detail=str(exc))
def run():
import uvicorn
port = int(os.getenv("ADDRESSGEN_PORT", "8732"))
uvicorn.run("backend.app:app", host="127.0.0.1", port=port, log_level="info")
if __name__ == "__main__":
run()

97
backend/p2pk.py Normal file
View File

@@ -0,0 +1,97 @@
import secrets
import hashlib
import json
import ecdsa
import base58
from typing import Dict
NETWORK_CONFIG = {
'mainnet': {'wif_prefix': b'\x80'},
'testnet': {'wif_prefix': b'\xEF'},
'regtest': {'wif_prefix': b'\xEF'},
}
def generate_p2pk(network: str = 'mainnet', compressed: bool = False) -> Dict[str, str]:
"""
Genera chiave privata, chiave pubblica e WIF per P2PK.
Args:
network: 'mainnet', 'testnet' o 'regtest'
compressed: True per chiave pubblica compressa (33 byte), False per non compressa (65 byte)
Returns:
Dizionario con 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'.")
private_key = secrets.token_bytes(32)
private_key_hex = private_key.hex()
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:]
prefix = b'\x02' if int.from_bytes(y, 'big') % 2 == 0 else b'\x03'
public_key = prefix + x
else:
public_key = b'\x04' + pubkey_bytes
public_key_hex = public_key.hex()
if compressed:
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]
wif = base58.b58encode(extended_key + checksum).decode()
return {
'network': network,
'script_type': 'p2pk',
'private_key_hex': private_key_hex,
'private_key_wif': wif,
'public_key_hex': public_key_hex
}
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')
try:
result = generate_p2pk(network, compressed)
print("\n--- Risultati ---")
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:
json.dump(result, f, indent=4)
print(f"Dati salvati correttamente nel file: {nome_file}")
except Exception as e:
print("Errore:", e)
if __name__ == '__main__':
main()

96
backend/p2pkh.py Normal file
View File

@@ -0,0 +1,96 @@
import secrets
import hashlib
import json
import ecdsa
import base58
from typing import Dict
NETWORK_CONFIG = {
'mainnet': {'addr_prefix': b'\x00', 'wif_prefix': b'\x80'},
'testnet': {'addr_prefix': b'\x6f', 'wif_prefix': b'\xEF'},
'regtest': {'addr_prefix': b'\x6f', 'wif_prefix': b'\xEF'},
}
def generate_legacy_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]:
"""Genera chiave privata, pubblica, WIF e indirizzo Bitcoin Legacy (P2PKH)."""
config = NETWORK_CONFIG.get(network)
if config is None:
raise ValueError("Network non supportato. Scegli tra 'mainnet', 'testnet' o 'regtest'.")
private_key = secrets.token_bytes(32)
private_key_hex = private_key.hex()
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:]
prefix = b'\x02' if int.from_bytes(y, 'big') % 2 == 0 else b'\x03'
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()
if compressed:
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()
return {
'network': network,
'script_type': 'p2pkh',
'private_key_hex': private_key_hex,
'private_key_wif': private_key_wif,
'public_key_hex': pubkey_hex,
'address': address
}
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()
compressed = compressed_input != 'n'
try:
result = generate_legacy_address(network, compressed)
print("\n--- Risultati ---")
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:
json.dump(result, f, indent=4)
print(f"Dati salvati correttamente nel file: {nome_file}")
except Exception as e:
print("Errore:", e)
if __name__ == '__main__':
main()

152
backend/p2sh.py Normal file
View File

@@ -0,0 +1,152 @@
import secrets
import hashlib
import json
import ecdsa
import base58
from typing import Dict, List
NETWORK_CONFIG = {
"mainnet": {"p2sh_prefix": b"\x05", "wif_prefix": b"\x80"},
"testnet": {"p2sh_prefix": b"\xC4", "wif_prefix": b"\xEF"},
"regtest": {"p2sh_prefix": b"\xC4", "wif_prefix": b"\xEF"},
}
def _to_wif(privkey: bytes, wif_prefix: bytes, compressed: bool = True) -> str:
"""Converte una chiave privata in WIF (aggiunge 0x01 se compressa)."""
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)."""
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
if compressed:
x = raw[:32]
y = raw[32:]
prefix = b"\x02" if int.from_bytes(y, "big") % 2 == 0 else b"\x03"
pub = prefix + x
else:
pub = b"\x04" + raw
return sk_bytes.hex(), pub.hex(), pub
def _op_push(data: bytes) -> bytes:
"""pushdata minimale (lunghezze pubkey/redeem < 0x4c gestite direttamente)."""
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 <pub1> ... <pubN> OP_n OP_CHECKMULTISIG."""
if not (1 <= m <= n <= 16):
raise ValueError("Richiesto 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)")
OP_CHECKMULTISIG = b"\xAE"
OP_m = bytes([0x50 + m]) # OP_1 .. OP_16
OP_n = bytes([0x50 + n])
script = OP_m
for pk in pubkeys:
script += _op_push(pk)
script += OP_n + OP_CHECKMULTISIG
return script
def _hash160(b: bytes) -> bytes:
return hashlib.new("ripemd160", hashlib.sha256(b).digest()).digest()
def _script_pubkey_p2sh(script_hash160: bytes) -> bytes:
# OP_HASH160 <20> <h160> OP_EQUAL
return b"\xA9\x14" + script_hash160 + b"\x87"
def _address_p2sh(h160: bytes, ver: bytes) -> str:
payload = ver + h160
checksum = hashlib.sha256(hashlib.sha256(payload).digest()).digest()[:4]
return base58.b58encode(payload + checksum).decode()
def generate_p2sh_multisig(
network: str = "mainnet",
m: int = 2,
n: int = 3,
compressed: bool = True,
sort_pubkeys: bool = True, # BIP67: ordina le pubkey per evitare malleabilità del redeem
) -> Dict:
"""Genera JSON per un P2SH multisig m-of-n (con chiavi locali)."""
cfg = NETWORK_CONFIG.get(network)
if cfg is None:
raise ValueError("Network non supportato (mainnet, testnet, regtest).")
if not (1 <= m <= n <= 16):
raise ValueError("Parametri m/n non validi (1 <= m <= n <= 16).")
# genera n coppie chiave
participants = []
pubkeys_bytes = []
for _ in range(n):
priv_hex, pub_hex, pub_bytes = _gen_keypair(compressed)
participants.append({
"private_key_hex": priv_hex,
"private_key_wif": _to_wif(bytes.fromhex(priv_hex), cfg["wif_prefix"], compressed),
"public_key_hex": pub_hex,
})
pubkeys_bytes.append(pub_bytes)
# BIP67: ordina le pubkey in modo deterministico (lexicografico sul byte array)
if sort_pubkeys:
pubkeys_bytes.sort()
redeem = _encode_multisig_redeem(m, pubkeys_bytes, n)
redeem_h160 = _hash160(redeem)
spk = _script_pubkey_p2sh(redeem_h160)
address = _address_p2sh(redeem_h160, cfg["p2sh_prefix"])
result = {
"network": network,
"script_type": "p2sh-multisig",
"m": m,
"n": n,
"redeem_script_hex": redeem.hex(),
"participants": participants,
"address": address
}
return result
def _redeem_asm(m: int, pubkeys: List[bytes], n: int) -> str:
"""Rappresentazione ASM comoda per debug."""
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()
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(f"{k}: {res[k]}")
print("\n-- Partecipanti --")
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:
json.dump(res, f, indent=4)
print(f"Salvato in {nome}")
except Exception as e:
print("Errore:", e)
if __name__ == "__main__":
main()

126
backend/p2tr.py Normal file
View File

@@ -0,0 +1,126 @@
import secrets, hashlib, json, base58
from typing import Dict, Optional
from ecdsa import SECP256k1, SigningKey
from ecdsa.ellipticcurve import Point
NETWORK_CONFIG = {
'mainnet': {'hrp': 'bc', 'wif_prefix': b'\x80'},
'testnet': {'hrp': 'tb', 'wif_prefix': b'\xEF'},
'regtest': {'hrp': 'bcrt', 'wif_prefix': b'\xEF'},
}
_BECH32_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def _bech32_polymod(values):
"""Calcola il polymod per bech32/bech32m"""
GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1
for v in values:
b = (chk >> 25) & 0xff
chk = ((chk & 0x1ffffff) << 5) ^ v
for i in range(5):
chk ^= GEN[i] if ((b >> i) & 1) else 0
return chk
def _bech32_hrp_expand(hrp):
"""Espande l'HRP per il calcolo bech32"""
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"""
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"""
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"""
acc = 0; bits = 0; ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for b in data:
if b < 0 or b >> frombits: return None
acc = ((acc << frombits) | b) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits: ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv): return None
return ret
def tagged_hash(tag: str, msg: bytes) -> bytes:
"""Calcola tagged hash BIP340"""
tagh = hashlib.sha256(tag.encode()).digest()
return hashlib.sha256(tagh + tagh + msg).digest()
curve = SECP256k1
G: Point = curve.generator
n = curve.order
def point_from_sk(sk_bytes: bytes) -> Point:
"""Genera punto pubblico da chiave privata"""
sk = int.from_bytes(sk_bytes, 'big')
if not (1 <= sk < n): raise ValueError("Chiave privata fuori 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)"""
return int(P.x()).to_bytes(32, 'big')
def pubkey_tweak(P: Point, merkle_root: Optional[bytes] = None):
"""Applica tweak Taproot al punto pubblico"""
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")
return P + t*G, t
def to_wif(privkey: bytes, wif_prefix: bytes) -> str:
"""Converte chiave privata in formato WIF"""
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"""
cfg = NETWORK_CONFIG.get(network)
if not cfg: raise ValueError("Network non supportato (mainnet, testnet, regtest).")
sk = secrets.token_bytes(32)
P = point_from_sk(sk)
Q, t = pubkey_tweak(P, merkle_root=None)
prog = xonly_bytes(Q)
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",
"private_key_hex": sk.hex(),
"private_key_wif": wif,
"internal_pubkey_x_hex": xonly_bytes(P).hex(),
"address": address
}
def main():
"""Funzione principale interattiva"""
net = input("Seleziona rete (mainnet/testnet/regtest): ").strip().lower()
try:
res = generate_p2tr_address(net)
print("\n--- Risultati P2TR ---")
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)
if __name__ == "__main__":
main()

97
backend/p2wpkh.py Normal file
View File

@@ -0,0 +1,97 @@
import secrets
import hashlib
import json
import ecdsa
import base58
from bech32 import bech32_encode, convertbits
from typing import Dict
NETWORK_CONFIG = {
'mainnet': {'hrp': 'bc', 'wif_prefix': b'\x80'},
'testnet': {'hrp': 'tb', 'wif_prefix': b'\xEF'},
'regtest': {'hrp': 'bcrt', 'wif_prefix': b'\xEF'},
}
def generate_segwit_address(network: str = 'mainnet', compressed: bool = True) -> Dict[str, str]:
"""Genera chiave privata, pubblica, WIF e indirizzo SegWit bech32."""
config = NETWORK_CONFIG.get(network)
if config is None:
raise ValueError("Network non supportato. Scegli tra 'mainnet', 'testnet' o 'regtest'.")
private_key = secrets.token_bytes(32)
private_key_hex = private_key.hex()
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:]
prefix = b'\x02' if int.from_bytes(y, 'big') % 2 == 0 else b'\x03'
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()
converted = convertbits(list(ripemd160), 8, 5)
if converted is None:
raise ValueError("Errore nella conversione dei bit per la codifica Bech32")
data = [0] + converted
address = bech32_encode(config['hrp'], data)
if compressed:
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()
return {
'network': network,
'script_type': 'p2wpkh',
'private_key_hex': private_key_hex,
'private_key_wif': private_key_wif,
'public_key_hex': pubkey_hex,
'address': address
}
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()
compressed = compressed_input != 'n'
try:
result = generate_segwit_address(network, compressed)
print("\n--- Risultati ---")
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:
json.dump(result, f, indent=4)
print(f"Dati salvati correttamente nel file: {nome_file}")
except Exception as e:
print("Errore:", e)
if __name__ == '__main__':
main()

2
backend/requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
fastapi
uvicorn[standard]