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 |
Language notes
Section titled “Language notes”- Rust: prefer
BTreeMap(or sort) overHashMapwhenever order reaches the output, an event or a hash, so native tests and the module behave the same.serde_json::Valueobjects are sorted maps by default, which makesserde_json::to_stringcanonical 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.*andtraceare rejected at build time. Usehive.log. - JavaScript engine:
Math.random(),Date.now(),Date()and argument-lessnew Date()throw.new Date(timestamp)works. Object key order follows insertion order, so build objects in a fixed order. - Python:
time,random,os,socketand threads are not importable. Dicts keep insertion order;setiteration order is not something to rely on: sort.
Check it
Section titled “Check it”ndsr run dist/m.hbc fn --input-file case.json | grep receipt_hashNDSR_ENGINE=pulley ndsr run dist/m.hbc fn --input-file case.json | grep receipt_hash # force the portable engineBoth 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.