Files
2026-06-29 01:30:12 +03:00

65 lines
2.1 KiB
Python

"""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)