Native Python tools for Nockchain wallet planning and transactions. Derive receiving addresses, inspect payments, select coins, and sign single-key or multisig spends. Python 3.11+; the package uses the standard library and has no runtime dependencies.
pip install nockchainThe remote Nockblocks MCP exposes v1_prepareTransaction through its search
and execute tools. Save the returned result.bundle object as bundle.json.
The package command runs locally without a native binary or a local MCP server:
pip install nockchain==0.3.0
python -m nockchain inspect bundle.json
python -m nockchain sign bundle.json --out signed.jsonThe installed nockchain command provides the same interface. sign shows the
locally reconstructed review and asks for SIGN before prompting for a hidden
zprv. It requires an interactive terminal. --key-file reads a local plain-text
zprv; --path derives an explicitly named child key. For recovery phrases, use
--mnemonic --path "m/..." and add --passphrase when needed. The path must
identify the key for the bundle's source address. Keep secrets out of MCP
requests, chats, shell arguments, and transaction files.
The command checks canonical encoding, existing signatures, full input
commitments, maturity, recipients, change, and the locally recomputed payment
plan and fee. Bundles support V1 inputs that one source key can spend, including
mature coinbase notes. A small remainder assigned to the fee is shown in
feeNicks. The output file has owner-only permissions and must not already
exist. The command does not broadcast. After explicit approval to submit, pass
the signed file's rawTransaction to v1_submitTransaction.
Network, tip, and input records come from the indexer. They are not proof of unspent status or finality, and the network label is not a consensus signature domain. Compare the review with the intended payment and a trusted chain-state source. The HTTP MCP returns data; it cannot launch the local command.
Applications use inspect_signing_bundle(value) for an InspectedSigningBundle
with bundle, transaction, and review, and sign_signing_bundle(value, key)
for a signed bundle. Both validate the bundle. Applications obtain user
authorization before calling the signing function.
verify_signatures(require_threshold=False) validates existing signatures and
fee coverage while allowing missing signatures.
from nockchain import derive_receive_address, parse_public_key
branch = parse_public_key(zpub) # rejects private extended keys
receive = derive_receive_address(branch.zpub(), 42)
print(receive.address, receive.lock_root)Reserve each receiving index durably before giving its address to a payer. Use a
dedicated non-hardened receiving branch and retain the path and allocated-index
range for recovery. address is the sending wallet's destination; lock_root is
its output lock commitment. A zpub is privacy-sensitive.
from nockchain import generate_wallet, restore_wallet
key, recovery = generate_wallet("") # optional BIP39 passphrase
# Back up recovery.words() privately before receiving funds. Never log it.
restored = restore_wallet(recovery.words(), "")
public_branch = key.derive("m/44'/0'/0'/9000").public().zpub()generate_mnemonic() creates a 24-word English BIP39 phrase. Recovery accepts
12–24 words, validates checksums, and normalizes Unicode with NFKD. A wrong
passphrase produces a different valid wallet. generate_seed() creates a raw
64-byte seed; master(seed_bytes) accepts one. Raw seeds have no mnemonic.
zprv(), words(), and Seed.to_bytes() explicitly export spending authority.
Diagnostic string and repr output redact secret-bearing objects.
Paths start with m and are relative to the supplied key. Apostrophes mark
hardened indices, which require private parents. public() removes the private
scalar. Key parsing validates canonical coordinates and the Cheetah prime-order
subgroup.
from nockchain import Payment, parse_nocks, select_transfer
nicks = parse_nocks("1.25") # 81920
plan = select_transfer(sender.public(), verified_unspent_notes, [
Payment(destination, "1.25"),
])
review = plan.summary()
# Present the review and obtain the wallet user's authorization before signing.
signed = plan.sign(sender)
wire = signed.jam()Amounts, heights, digest limbs, and atoms are Python integers. Floats and booleans
are rejected at numeric protocol boundaries. 1 NOCK = 65,536 nicks;
parse_nocks() rejects sub-nick decimal precision, and format_nocks() is exact.
select_transfer() chooses sufficient notes largest-first with deterministic
note-name ties. prepare_transfer() consumes every supplied record. Both merge
repeated recipients, estimate fees, and send change to the sender unless
TransferOptions(change_address=...) is set. A nonzero fee_nicks supplies an
explicit fee. With automatic fees, a remainder too small to pay for change
metadata becomes part of the fee; the review shows the exact amount.
These helpers use the signing-key matcher; supply a chain context for mature coinbase inputs. sign_transfer_v1() accepts explicit
per-input output and fee allocations.
An UnspentNote holds the complete note and its verified lock root:
from nockchain import NoteV1, NoteName, UnspentNote, cue, parse_digest
note = NoteV1(
origin_page=100,
name=NoteName(parse_digest(first), parse_digest(last)),
note_data=cue(stored_metadata_jam),
nicks=10_000_000,
)
unspent = UnspentNote(note, parse_digest(input_lock_root))Parent commitments include version, origin, name, all stored metadata, and
value. Empty metadata is 0 only when the stored record actually has none.
The application authenticates input existence, unspent status, and confirmations.
from nockchain import ChainContext, PlannedOutput, PlanRequest, SigningKeyLockMatcher, plan
result = plan(PlanRequest(
candidates=tuple(verified_notes),
outputs=(PlannedOutput.payment(recipient_pkh, 1_000_000),),
refund=PlannedOutput(sender_lock.root, sender_lock.metadata(), 0),
chain=ChainContext.at_height(chain_height),
), SigningKeyLockMatcher(sender_pkh))
review = result.transaction.summary()
# Review selected inputs, final_fee, minimum_fee, refunds, and word_counts.
signed = result.transaction.sign(sender)Use selection, order, fee_nicks, or sweep in PlanRequest to control
selection and allocation. LockRootLockMatcher supplies lock, leaf_number,
preimages, and an optional fund_policy. LockTreeV1, LockMerkleProof, and
build_lock_merkle_proof expose the same policies at the transaction boundary.
The pure planner selects eligible notes, reserves a positive seed for every input, assigns payments and refunds, and solves the fee from the encoded transaction. Selection sorts by amount and then Base58 note name. An explicit selection requires every chosen note to be eligible. A sweep consumes all eligible candidates into one destination. An exact fee remains visible in the review even when it is below the required minimum; signing rejects an underfunded transaction.
The signing-key matcher handles single-signature PKH policies and the coinbase relative-age policy. The root matcher selects a specific leaf of a disclosed lock, checks hashlock preimages, and can use an explicitly configured fund root, authoritative first name, and maturity policy. Fund maturity uses the candidate height. Fee parameters independently select the fee schedule; the height helper sets both together. With no candidate height, timed and fund inputs are ineligible.
Lock trees contain up to 16 leaves and pad to a power of two with burn conditions. Leaf numbers start at 1. Conditions support PKH, timelock, hashlock, and burn primitives; a burn condition cannot be spent. Proofs preserve the outer tree tag, axis, and sibling path. Before Bythos, ordinary inputs use single-leaf stub proofs; at Bythos they use full proofs. Refund metadata preserves the entire chosen output policy, including trees and custom entries.
NoteData provides canonical metadata maps, hashing, word counts, and typed
LockData, BridgeDepositDataPayload, and BridgeWithdrawalDataPayload values.
Deposit addresses are exactly 20 big-endian bytes. Unknown versions and networks
remain raw nouns. compute_bridge_fee rounds up to whole NOCK before applying
the rate and saturates at uint64 limits. Construct an opaque output with a
Digest as lock and explicit note_data; full lock forms disclose their
metadata by default. Ethereum transaction construction and submission belong
to the caller.
NoteV0.from_noun reads complete V0 records, including public-key policy, source,
coinbase flag, and optional timelock. Set version=0 in PlanRequest to migrate
those notes into V1 outputs. When parsing a migration transaction, supply
migration_notes=verified_v0_notes, fees=fees to parse_transaction_v1; the wire
witness does not contain the V0 input policy. Parent validation commits to the
complete supplied record. V0 and V1 candidates are selected separately.
from nockchain import (
HeightRange, TimelockV1, LockV1, LockedPaymentV1, LockedSpendPlanV1,
minimum_transaction_fee_v1, prepare_transaction_v1,
)
lock = LockV1(2, (alice_pkh, bob_pkh, carol_pkh), TimelockV1(
relative=HeightRange(min=10),
absolute=HeightRange(max=500_000),
))
fee = minimum_transaction_fee_v1([lock], [recipient_lock])
unsigned = prepare_transaction_v1([LockedSpendPlanV1(
note=verified_note, lock=lock, fee=fee,
outputs=(LockedPaymentV1(recipient_lock, verified_note.nicks - fee),),
)])
# Each signer independently reviews the same unsigned plan.
alice = unsigned.sign(alice_key)
bob = unsigned.sign(bob_key)
signed = alice.merge_signatures(bob)
signed.verify_signatures()
signed.validate_inputs([verified_note], chain_height)A lock requires exactly m signatures, with up to 32 distinct keys. Extra
signatures are rejected. Timelock endpoints are inclusive; None is unbounded.
Relative heights measure age since the note's origin page. Signing does not
assert that a timelock is satisfied. Each signature changes the transaction ID.
Partial transactions travel as jam bytes; merging rejects altered plans and
conflicting signatures. Plans, notes, policies, and reviews are immutable.
from nockchain import parse_transaction_v1
tx = parse_transaction_v1(wire)
review = tx.summary()
tx.verify_signatures()
tx.validate_inputs(verified_notes, chain_height)
print(str(tx.id))| Operation | What it establishes |
|---|---|
parse_transaction_v1() |
Supported structure, canonical trees, proof roots, key encoding, and embedded ID |
summary() |
Committed outputs, fees, signature hashes, and present signers |
OutputV1.lock() |
Metadata authenticates the committed output policy |
verify_signatures() |
Every signature, authorization, exact threshold, and minimum fee |
validate_inputs() |
Complete supplied records, parent hashes, balances, origin heights, and timelocks |
Validation methods raise NockchainError on protocol failures and return None
on success. verify_digest() returns a boolean. Successful parsing is not proof
of valid signatures or chain state. The package does not fetch chain data,
broadcast transactions, or implement confirmation policy.
Digest is an immutable tuple of five field elements. str(digest) encodes its
Base58 form and parse_digest() validates it. Noun is an integer atom or an
immutable Cell. Use atom(), Cell(head, tail), noun_tuple(), and noun_list()
for construction; hash_words(), hash_noun(), jam(), and cue() implement the
hashing and codec primitives. Atom values must be below the Goldilocks prime;
these are based nouns, not arbitrary-size Nock atom containers.
Noun hashing and jam/cue traversal are iterative. Shared subtrees count toward logical expansion limits even when backreferences make the wire encoding small. Equality memoizes visited cell pairs. General noun operations allow 1,048,576 logical nodes and at most 16 MiB of jam bytes.
Transactions support V1 lock proofs and V0-to-V1 migration spends. Outputs have unconstrained source fields.
Limits are 64 inputs, 256 output seeds, 32 keys per lock, 64 metadata entries, 2,048 flattened metadata words per output lock, 65,536 transaction nodes, and depth 256. Every input retains at least one nonzero output seed.
Fee parameters control the phase height, base rate, witness divisor, and minimum. At Bythos, metadata is merged per output lock and the witness discount applies. Before Bythos, metadata is counted per seed and the base rate doubles. The default fee context is height 54,000; provide the actual chain context when planning. Conflicting metadata is rejected. Applications track network rule changes.
Private-key arithmetic is variable-time; use trusted offline signing tools. See SECURITY.md.
python -m venv .venv
. .venv/bin/activate
pip install -r requirements-dev.txt
pip install -e .
python -m unittest discover -s tests -v
ruff check src tests
ruff format --check src tests
python -m build
python -m twine check dist/*The public corpus checks 35 wallet vectors, 105 signatures, 24 BIP39 vectors, 13 TIP5 vectors, seven noun encodings, four single-key transactions, and two multisig transactions. Tests compare exact partial and signed bytes and the canonical Hoon oracle's IDs, complete note commitments, and minimum fees. Additional tests cover malformed input, immutable reviews, timelock boundaries, metadata, fees, and selection. CI tests Python 3.11 and 3.13, then builds and installs the wheel. Normal tests are offline and require no Go, Rust, or Hoon.
Wallet behavior is checked against the official Hoon source at
9bcc26c
and the shared Rust wallet core at
02d1946.
The wallet corpus covers all 31 tagged lock leaves, metadata and bridge payloads,
phase-dependent fees, seven planner scenarios, and four V0 migration scenarios
with exact unsigned and signed bytes. Fixture files retain their source pins and
SHA-256 checksums. The Go repository contains the reproducible generators;
normal SDK tests use checked-in data and run offline.
Never send funds to public test-vector addresses.
MIT licensed. See THIRD_PARTY_LICENSES for wordlist attribution.