Receipts
Every execution produces a receipt. Its hash covers only consensus fields (no timestamp, no node id), so every honest node computes the same hash for the same call.
receipt_hash = keccak256(canonical_json({ "v": 1, "module_address": "0x…", // lowercase "function": "name", "input_hash": "0x…", // keccak256(raw input bytes) "output_hash": "0x…", // keccak256(raw output bytes); empty output on failure "events_hash": "0x…", // keccak256(canonical_json([{"name","data"}, …])), [] on failure "gas_used": 1234, "success": true}))- The
errorstring of a failed call is informational and not hashed. - A node signs
"necter-receipt-v1:" + receipt_hashwith its ed25519 key. The signed envelope carries the receipt,node_id,public_key,timestampandsignature. node_id = "ndsr-"+ the first 16 hex characters ofkeccak256(public key).
Verifying a result
Section titled “Verifying a result”Given an output you received and the receipt hash a quorum agreed on, you can check:
input_hash == keccak256(the input you sent)andoutput_hash == keccak256(output).- Recompute
receipt_hashfrom the fields with canonical JSON and Keccak-256. - For direct calls (
/v1/execute) the response lists the agreeing validators. For committee tasks,GET /v1/explorer/rounds/{round_id}andGET /v1/explorer/receipts/{receipt_hash}show the votes and the validators’ finality record. - Or re-run it:
ndsr run module.hbc fn --input '…'gives the samereceipt_hashfor a stateless call.
import jsonfrom eth_hash.auto import keccak # pip install "eth-hash[pycryptodome]"
def h(b: bytes) -> str: return "0x" + keccak(b).hex()
def canonical(o) -> bytes: return json.dumps(o, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
receipt = { "v": 1, "module_address": "0x…", "function": "work", "input_hash": h(input_bytes), "output_hash": h(output_bytes), "events_hash": h(canonical(events)), "gas_used": gas_used, "success": True,}assert h(canonical(receipt)) == receipt_hashVotes and rounds
Section titled “Votes and rounds”Receipts are what nodes vote on:
| Round id | Who votes | Where |
|---|---|---|
exec:0x… |
Validators | Direct calls (/v1/execute) and validator fallback |
task:0x… |
Committee miners, then validators sign a finality vote | Mining projects |
epoch:<project>:<epoch>:<attempt> |
Validators | Per-project reward receipts |
A vote also carries a round signature over a short text (for example
necter-vote-v1\ntask\n<round_id>\n<receipt_hash>\n<payout_address>), binding the vote to one round and one
payout address so it cannot be replayed. Two votes from one key in one round with different receipt
hashes are equivocation, which is provable and slashable. Slashing