| tags | cyber, rs, reference |
|---|
Blockchain consensus requires that every node produces identical output for identical input. Rust does not guarantee this. Floating point operations can produce different results on different architectures. Integer overflow behavior depends on debug/release mode. Memory layout of structs is unspecified.
The #[deterministic] attribute marks functions that must produce identical results on all platforms.
#[deterministic]
fn compute_rank(weights: &[FixedPoint<u128, 18>]) -> Option<FixedPoint<u128, 18>> {
let mut sum = FixedPoint::ZERO;
for w in weights {
sum = sum.checked_add(*w)?; // checked arithmetic required
}
Some(sum)
}Inside a #[deterministic] function, the compiler rejects:
| Rejected Construct | Reason | Error Code |
|---|---|---|
f32, f64 types |
Non-deterministic across platforms | RS201 |
as casts involving floats |
Rounding is platform-dependent | RS202 |
| Raw pointer arithmetic | Addresses are non-deterministic | RS203 |
std::time::Instant |
Wall clock is non-deterministic | RS204 |
rand::* |
Randomness is non-deterministic | RS205 |
Unchecked arithmetic (+, -, *) |
Overflow behavior differs debug/release | RS206 |
HashMap iteration |
Order is non-deterministic | RS207 |
| Inline assembly | Platform-specific by definition | RS208 |
Calling non-#[deterministic] functions |
Transitivity requirement | RS209 |
usize, isize types |
Width is platform-dependent (32 or 64 bit) | RS210 |
FixedPoint<T, DECIMALS>(Rs built-in fixed-point type)checked_add,checked_mul,checked_sub,checked_divsaturating_*arithmeticwrapping_*arithmetic (deterministic across platforms)overflowing_*arithmetic (returns value + overflow flag)BTreeMap,BTreeSet(deterministic iteration order) — inedition = "rs", useBoundedMap/BoundedSetinstead (stack-allocated);BTreeMaprequires#[allow(rs::heap)]- Arrays, slices with deterministic indexing
- Other
#[deterministic]functions const fn(already deterministic)- All comparison and logical operations
Determinism is contagious upward and required downward:
#[deterministic]
fn outer() -> u64 {
inner() // OK only if inner() is also #[deterministic]
}
#[deterministic]
fn inner() -> u64 {
42
}
fn non_det() -> u64 {
outer() // OK: non-deterministic can call deterministic
}Rs provides a built-in fixed-point numeric type:
// FixedPoint<BaseType, DecimalPlaces>
type Rank = FixedPoint<u128, 18>; // 18 decimal places, u128 backing
let a: Rank = Rank::from_integer(42);
let b: Rank = Rank::from_raw(42_000_000_000_000_000_000u128); // 42.0
let c = a.checked_add(b).unwrap();
let d = a.checked_mul(b).unwrap();
// All operations are deterministic, checked, no floatsCompiler implementation: ~400 lines (lint pass + diagnostics).
See errors/deterministic.md for detailed descriptions of RS201–RS210.