Skip to content

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 error string of a failed call is informational and not hashed.
  • A node signs "necter-receipt-v1:" + receipt_hash with its ed25519 key. The signed envelope carries the receipt, node_id, public_key, timestamp and signature.
  • node_id = "ndsr-" + the first 16 hex characters of keccak256(public key).

Given an output you received and the receipt hash a quorum agreed on, you can check:

  1. input_hash == keccak256(the input you sent) and output_hash == keccak256(output).
  2. Recompute receipt_hash from the fields with canonical JSON and Keccak-256.
  3. For direct calls (/v1/execute) the response lists the agreeing validators. For committee tasks, GET /v1/explorer/rounds/{round_id} and GET /v1/explorer/receipts/{receipt_hash} show the votes and the validators’ finality record.
  4. Or re-run it: ndsr run module.hbc fn --input '…' gives the same receipt_hash for a stateless call.
import json
from 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_hash

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