"""Solana wallet management — load or generate an Ed25519 keypair. Solana keypair format: 64-byte array stored as JSON list of ints. bytes 0-31: private scalar (seed) bytes 32-63: public key (on-curve point) Wallet address: base58-encoded 32-byte public key. """ import json from pathlib import Path _DEFAULT_WALLET_PATH = Path.home() / ".config" / "meshnet" / "wallet.json" _B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" def _b58encode(data: bytes) -> str: n = int.from_bytes(data, "big") chars: list[str] = [] while n: n, rem = divmod(n, 58) chars.append(_B58_ALPHABET[rem]) leading = 0 for b in data: if b == 0: leading += 1 else: break return "1" * leading + "".join(reversed(chars)) def load_or_create_wallet( path: Path = _DEFAULT_WALLET_PATH, ) -> tuple[bytes, bytes, str]: """Return (secret_32, public_32, address_base58). Loads from *path* if it exists; otherwise generates a new keypair and saves it. The parent directory is created if needed. """ if path.exists(): mode = path.stat().st_mode & 0o777 if mode & 0o077: path.chmod(0o600) raw = bytes(json.loads(path.read_text())) if len(raw) != 64: raise ValueError(f"Wallet at {path} has unexpected length {len(raw)}, expected 64") secret, public = raw[:32], raw[32:] return secret, public, _b58encode(public) from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.serialization import ( Encoding, NoEncryption, PrivateFormat, PublicFormat, ) priv = Ed25519PrivateKey.generate() secret = priv.private_bytes(Encoding.Raw, PrivateFormat.Raw, NoEncryption()) public = priv.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw) keypair = list(secret) + list(public) path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(keypair)) path.chmod(0o600) return secret, public, _b58encode(public)