Summary
Two host functions inject per-node nondeterminism into guest execution:
time_now calls SystemTime::now() (wall clock).
random_bytes pulls from rand::thread_rng() (OS entropy).
In a replicated state machine every node must derive identical state from identical inputs. Both of these let two honest nodes running the same call observe different values, which can cause state divergence.
Severity: High (determinism in a replicated state machine).
Location
crates/runtime/src/logic/host_functions/utility.rs — time_now (SystemTime::now().duration_since(UNIX_EPOCH)) and random_bytes (rand::thread_rng().fill_bytes(...)).
- Import wiring:
crates/runtime/src/logic/imports.rs (fn random_bytes(ptr: u64);, fn time_now(ptr: u64);).
Details
The fix direction is fairly clear, but it is a guest-observable semantics change, which is why it was deferred for explicit sign-off rather than bundled with the mechanical safety fixes.
Possible solutions
-
time_now → derive from the HLC.
Use the hybrid logical clock instead of the wall clock. calimero_storage::env::hlc_timestamp() already exists and is used by persist_root_state (see the hlc_to_nanos helper there). This changes what "now" means for every guest but makes it deterministic across nodes.
-
random_bytes → seed a deterministic RNG from a context-deterministic source.
Replace OS entropy with a deterministic RNG (e.g. ChaCha) seeded from context_id + executor_public_key + a per-call counter (all available on VMLogic/VMContext). Reproducible across nodes — which is the point for consensus, but a footgun for apps that (mis)use it for secrets/nonces.
- Consider whether to expose a separate, explicitly non-deterministic entropy escape hatch for the rare legitimate case, so the default stays safe.
Open questions
- Is determinism required for these functions in the current execution model, or are there call sites where real time/entropy is intentional and non-consensus?
- Exact seed derivation for the deterministic RNG (inputs + per-call counter scheme).
- Whether a separate insecure-random API is desirable.
Context
Split out from the host-function safety batch in #3097 (the four low-risk fixes landed there). Deferred because it changes guest-observable semantics.
Summary
Two host functions inject per-node nondeterminism into guest execution:
time_nowcallsSystemTime::now()(wall clock).random_bytespulls fromrand::thread_rng()(OS entropy).In a replicated state machine every node must derive identical state from identical inputs. Both of these let two honest nodes running the same call observe different values, which can cause state divergence.
Severity: High (determinism in a replicated state machine).
Location
crates/runtime/src/logic/host_functions/utility.rs—time_now(SystemTime::now().duration_since(UNIX_EPOCH)) andrandom_bytes(rand::thread_rng().fill_bytes(...)).crates/runtime/src/logic/imports.rs(fn random_bytes(ptr: u64);,fn time_now(ptr: u64);).Details
The fix direction is fairly clear, but it is a guest-observable semantics change, which is why it was deferred for explicit sign-off rather than bundled with the mechanical safety fixes.
Possible solutions
time_now→ derive from the HLC.Use the hybrid logical clock instead of the wall clock.
calimero_storage::env::hlc_timestamp()already exists and is used bypersist_root_state(see thehlc_to_nanoshelper there). This changes what "now" means for every guest but makes it deterministic across nodes.random_bytes→ seed a deterministic RNG from a context-deterministic source.Replace OS entropy with a deterministic RNG (e.g. ChaCha) seeded from
context_id+executor_public_key+ a per-call counter (all available onVMLogic/VMContext). Reproducible across nodes — which is the point for consensus, but a footgun for apps that (mis)use it for secrets/nonces.Open questions
Context
Split out from the host-function safety batch in #3097 (the four low-risk fixes landed there). Deferred because it changes guest-observable semantics.