Skip to content

Determinism rules

NDSR executes Hive Bytecode deterministically, but your code still decides what it computes. A module whose output depends on anything outside its input and state either fails to build, fails at run time, or produces votes that disagree (and on a mining project, disagreeing votes can be slashed).

Don’t Why Do instead
Read the clock (Date.now(), new Date(), std::time, time.time()) Every node runs at a different moment; there is no clock import Pass timestamps in the input. Scheduled projects get the epoch and slot in their input
Use randomness (Math.random(), rand, random) No entropy is available Derive values with hash(seed ‖ context); use the seed that scheduled tasks receive, or a commit-reveal value in the input
Do I/O (files, network, environment variables, threads) No such imports exist; WASI modules are rejected Fetch data off-chain and pass it in the input (and verify it: signatures, hashes)
Put floats in events or signed data Canonical JSON has no floats; the call fails Integer minor units (3200120000 micro-USD), basis points, or strings
Depend on hash-map iteration order Go maps and some hash maps iterate in varying order Sort keys before iterating or serializing (BTreeMap in Rust, sort.Strings in Go)
Rely on floating-point results across languages Rounding and formatting differ between language runtimes Use integers for anything you compare, hash or pay on; floats in plain outputs are fine but formatting is language-specific
Use unbounded recursion or loops on user input Gas and stack limits stop the call, failing it Bound input sizes (count, length) and validate early
Return non-UTF-8 output Outputs must be valid UTF-8 Hex or base64-encode binary data
Assume call order between separate requests Requests to a stateful module are ordered by the Hub, but your client may send them concurrently Make state changes idempotent (request ids) and check preconditions inside the call
  • Rust: prefer BTreeMap (or sort) over HashMap whenever order reaches the output, an event or a hash, so native tests and the module behave the same. serde_json::Value objects are sorted maps by default, which makes serde_json::to_string canonical for integer-only data.
  • Go: never range over a map to build output, events or hashes without sorting the keys. Panics trap; use errors.
  • AssemblyScript: Math.random, Date, console.* and trace are rejected at build time. Use hive.log.
  • JavaScript engine: Math.random(), Date.now(), Date() and argument-less new Date() throw. new Date(timestamp) works. Object key order follows insertion order, so build objects in a fixed order.
  • Python: time, random, os, socket and threads are not importable. Dicts keep insertion order; set iteration order is not something to rely on: sort.
Terminal window
ndsr run dist/m.hbc fn --input-file case.json | grep receipt_hash
NDSR_ENGINE=pulley ndsr run dist/m.hbc fn --input-file case.json | grep receipt_hash # force the portable engine

Both must print the same hash, and so must a colleague’s machine. On the testnet, /v1/execute answers only when 3 of 4 validators agree, so non-determinism shows up as 502 no_consensus.