Wallets, key files, and transactions
Create or import a wallet
KeyStore uses English BIP39 mnemonics and SLIP-0010 Ed25519 derivation at
m/44'/73404'/{account}'.
from znn.wallet.keystore import KeyStore
store = KeyStore.new_random()
first = store.get_key_pair(0)
second = store.get_key_pair(1)
print(store.mnemonic) # back this up securely; never log it in production
print(first.address)
Import an existing mnemonic or the stable 16/32-byte entropy profile:
store = KeyStore.from_mnemonic(mnemonic, passphrase="optional BIP39 passphrase")
store = KeyStore.from_entropy(entropy_hex)
Account indices must be non-negative integers. Call clear() on key stores and
key pairs when you no longer need their in-memory secret material.
Keys and signatures
from znn.wallet.keypair import KeyPair, verify_signature
keypair = KeyPair(private_key_hex)
# Legacy text signing returns a base64-encoded signature.
signature = keypair.sign("Hello, aliens").decode()
verify_signature(keypair.public_key, signature, "Hello, aliens")
# Protocol signing uses the raw 32-byte hash and returns 64 raw signature bytes.
block_signature = keypair.sign_hash(block_hash)
KeyPair accepts only 32-byte Ed25519 private keys. Account-block signing does
not base64-wrap or transform the canonical hash before signing.
Encrypted key files
Version-one key files use Argon2id and AES-256-GCM with authenticated data
zenon.
import json
from znn.wallet.keyfile import decrypt, encrypt, needs_upgrade
document = encrypt(store, password, timestamp=1_700_000_000)
serialized = json.dumps(document)
restored = decrypt(json.loads(serialized), password)
assert restored.get_key_pair(0).address == store.get_key_pair(0).address
if needs_upgrade(document):
document = encrypt(restored, password)
Customize Argon2 work factors when required by your application:
document = encrypt(
store,
password,
kdf_config={
"timeCost": 2,
"memoryCost": 131072,
"hashLength": 32,
"parallelism": 4,
},
)
Malformed documents, unsupported algorithms, invalid parameters, bad
passwords, and corrupted ciphertext raise KeyFileError. Keep the full
document, including baseAddress, timestamp, version, and every declared
crypto field.
Prepare before publishing
Transaction preparation and publication are separate operations. Preparation:
- reads the account and momentum frontiers;
- validates receive blocks when applicable;
- requests the required plasma/PoW values;
- applies fused plasma or generates and verifies PoW;
- hashes the canonical account-block preimage; and
- signs the raw hash with Ed25519.
from znn.api.embedded.plasma import PlasmaApi
from znn.api.ledger import LedgerApi
from znn.model.nom.account_block import AccountBlock
from znn.model.primitives.address import Address
from znn.model.primitives.token_standard import ZNN_ZTS
from znn.wallet.transact import Transact
block = AccountBlock.send(
Address.parse("z1qzpcwr6lk0zhejzpt04j2jcqqadtu046ffr8nr"),
ZNN_ZTS,
100_000_000,
)
tx = Transact(
private_key_hex,
ledger_api=LedgerApi(client),
plasma_api=PlasmaApi(client),
)
prepared = await tx.prepare_block(block)
# Inspect or transfer `prepared` to another policy layer here.
published = await tx.publish_block(prepared)
publish_block() requires the canonical null success response and returns the
prepared block. fast_forward_block() combines preparation and publication.
Convenience methods send(), receive(), and call_contract() build and
fast-forward their respective blocks.
High-level SDK transaction flow
from znn import Zenon
from znn.model.nom.account_block import AccountBlock
sdk = Zenon()
await sdk.initialize("wss://your-node.example:35998")
try:
block = AccountBlock.send(to_address, token_standard, amount)
published = await sdk.send(block, keypair)
finally:
await sdk.disconnect()
Zenon.prepare_block() is also available when you need only a prepared block.
Use the explicit Transact.prepare_block() / publish_block() split when that
prepared block must later be published unchanged.
Custom PoW providers
A provider receives (data_hash_hex, difficulty) and returns a 16-character
lowercase nonce hex string. It may be synchronous or asynchronous.
async def remote_pow(data_hash, difficulty):
return await trusted_pow_service(data_hash, difficulty)
sdk.set_pow_provider(remote_pow)
Returned nonces are length-checked and verified locally. A preselected nonce and difficulty are also accepted when they satisfy at least the node-required difficulty.
The example cookbook includes complete environment-driven wallet, key-file, embedded builder, prepare/publish, high-level SDK, ABI, and PoW programs.