diff --git a/.github/workflows/spec-drift-guard.yml b/.github/workflows/spec-drift-guard.yml index a88130a4..d68ed213 100644 --- a/.github/workflows/spec-drift-guard.yml +++ b/.github/workflows/spec-drift-guard.yml @@ -221,6 +221,30 @@ jobs: fi exit $STATUS + - name: Cargo test targets — every tests/*.rs must be declared + working-directory: tri-net + run: | + # Cargo.toml sets `autotests = false`, so a test file is compiled ONLY + # if it has an explicit [[test]] block. A file dropped into tests/ is + # therefore silently never run -- it LOOKS like coverage and is not. + # That is how tests/compute_ring_invariants.rs (18 load-bearing + # invariants, written precisely because spec test blocks are not + # executed) sat dead in the repo. Fail loudly on any undeclared file. + STATUS=0 + declared=$(grep -oE 'tests/[a-z0-9_]+\.rs' Cargo.toml | sort -u) + present=$(ls tests/*.rs | sort) + missing=$(comm -13 <(printf '%s\n' "$declared") <(printf '%s\n' "$present")) + if [ -n "$missing" ]; then + echo "::error::these test files are NOT declared in Cargo.toml and never run:" + echo "$missing" | sed 's/^/ - /' + echo "Add a [[test]] block (name + path) for each, or delete the file." + STATUS=1 + fi + n_decl=$(printf '%s\n' "$declared" | sed '/^$/d' | wc -l | tr -d ' ') + n_file=$(printf '%s\n' "$present" | sed '/^$/d' | wc -l | tr -d ' ') + echo "cargo test targets: ${n_file} files, ${n_decl} declared" + exit $STATUS + - name: Zig test — execute committed gens (passing set) working-directory: tri-net run: | @@ -297,7 +321,7 @@ jobs: STATUS=1 fi done <<'BACKENDS' - rust rs gen-rust 86 + rust rs gen-rust 96 zig zig gen 107 c c gen-c 75 BACKENDS diff --git a/Cargo.toml b/Cargo.toml index 5e67ba97..01774446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -354,3 +354,7 @@ path = "tests/dispute_witness.rs" [[test]] name = "witness_economics" path = "tests/witness_economics.rs" + +[[test]] +name = "compute_ring_invariants" +path = "tests/compute_ring_invariants.rs" diff --git a/gen/rust/tri_a2a.rs b/gen/rust/tri_a2a.rs new file mode 100644 index 00000000..d31d9f3c --- /dev/null +++ b/gen/rust/tri_a2a.rs @@ -0,0 +1,601 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const KIND_DATA: u32 = 1; + +pub const A2A_PORT: u32 = 0xA2A; + +pub const MSG_TASK_ASSIGN: u32 = 1; + +pub const MSG_TASK_RESULT: u32 = 2; + +pub const MSG_HEARTBEAT: u32 = 3; + +pub const SKILL_GF16_MUL: u32 = 0x1611; + +pub const SKILL_GF16_ADD: u32 = 0x1610; + +pub const SKILL_GFT16_MUL: u32 = 0xA611; + +pub const SKILL_GFT16_ADD: u32 = 0xA610; + +pub const SKILL_GFT8_MUL: u32 = 0xA811; + +pub const SKILL_GFT8_ADD: u32 = 0xA810; + +pub const SKILL_GFT4_MUL: u32 = 0xA411; + +pub const SKILL_GFT4_ADD: u32 = 0xA410; + +pub const SKILL_GFT32_MUL: u32 = 0xA511; + +pub const SKILL_GFT32_ADD: u32 = 0xA510; + +pub const SKILL_GFT64_MUL: u32 = 0xA311; + +pub const SKILL_GFT64_ADD: u32 = 0xA310; + +pub const SKILL_GFT128_MUL: u32 = 0xA211; + +pub const SKILL_GFT128_ADD: u32 = 0xA210; + +pub fn is_a2a(kind: u32, port: u32) -> bool { + if (kind == KIND_DATA) { + return (port == A2A_PORT); + } else { + return (port != port); + } +} + +pub fn carries_receipt(msg_class: u32) -> bool { + return (msg_class == MSG_TASK_RESULT); +} + +pub fn is_gf_skill(skill: u32) -> bool { + if (skill == SKILL_GF16_MUL) { + return (skill == skill); + } else { + return (skill == SKILL_GF16_ADD); + } +} + +pub fn is_gft_skill(skill: u32) -> bool { + if (skill == SKILL_GFT16_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT16_ADD) { + return (skill == skill); + } + if (skill == SKILL_GFT8_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT8_ADD) { + return (skill == skill); + } + if (skill == SKILL_GFT4_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT4_ADD) { + return (skill == skill); + } + if (skill == SKILL_GFT32_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT32_ADD) { + return (skill == skill); + } + if (skill == SKILL_GFT64_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT64_ADD) { + return (skill == skill); + } + if (skill == SKILL_GFT128_MUL) { + return (skill == skill); + } + if (skill == SKILL_GFT128_ADD) { + return (skill == skill); + } + return (skill != skill); +} + +pub const FMT_GF_BINARY: u32 = 0; + +pub const FMT_GFT: u32 = 1; + +pub fn skill_family(skill: u32) -> u32 { + if (skill == SKILL_GFT16_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT16_ADD) { + return FMT_GFT; + } + if (skill == SKILL_GFT8_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT8_ADD) { + return FMT_GFT; + } + if (skill == SKILL_GFT4_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT4_ADD) { + return FMT_GFT; + } + if (skill == SKILL_GFT32_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT32_ADD) { + return FMT_GFT; + } + if (skill == SKILL_GFT64_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT64_ADD) { + return FMT_GFT; + } + if (skill == SKILL_GFT128_MUL) { + return FMT_GFT; + } + if (skill == SKILL_GFT128_ADD) { + return FMT_GFT; + } + return FMT_GF_BINARY; +} + +pub fn family_matches(assigned_skill: u32, receipt_family: u32) -> bool { + return (skill_family(assigned_skill) == receipt_family); +} + +pub fn is_hosted_skill(skill: u32) -> u32 { + if (skill == SKILL_GF16_MUL) { + return 1; + } + if (skill == SKILL_GF16_ADD) { + return 1; + } + if (skill == SKILL_GFT16_MUL) { + return 1; + } + if (skill == SKILL_GFT16_ADD) { + return 1; + } + if (skill == SKILL_GFT8_MUL) { + return 1; + } + if (skill == SKILL_GFT8_ADD) { + return 1; + } + if (skill == SKILL_GFT4_MUL) { + return 1; + } + if (skill == SKILL_GFT4_ADD) { + return 1; + } + if (skill == SKILL_GFT32_MUL) { + return 1; + } + if (skill == SKILL_GFT32_ADD) { + return 1; + } + if (skill == SKILL_GFT64_MUL) { + return 1; + } + if (skill == SKILL_GFT64_ADD) { + return 1; + } + if (skill == SKILL_GFT128_MUL) { + return 1; + } + if (skill == SKILL_GFT128_ADD) { + return 1; + } + return 0; +} + +pub fn family_matches_strict(assigned_skill: u32, receipt_family: u32) -> bool { + if (is_hosted_skill(assigned_skill) == 1) { + return (skill_family(assigned_skill) == receipt_family); + } else { + return (assigned_skill != assigned_skill); + } +} + +pub fn skill_op(skill: u32) -> u32 { + return (skill & 0xFF); +} + +pub fn result_matches_assign(assign_task_id: u32, result_task_id: u32, receipt_task: u32) -> bool { + if (assign_task_id == result_task_id) { + return (result_task_id == receipt_task); + } else { + return (result_task_id != result_task_id); + } +} + +pub fn is_fresh(task_id: u32, last_settled: u32) -> bool { + return (task_id > last_settled); +} + +pub fn next_watermark(task_id: u32, last_settled: u32) -> u32 { + if (task_id > last_settled) { + return task_id; + } else { + return last_settled; + } +} + +pub fn next_watermark_settled(task_id: u32, last_settled: u32, settled: u32) -> u32 { + if (settled == 1) { + if (task_id > last_settled) { + return task_id; + } else { + return last_settled; + } + } else { + return last_settled; + } +} + +pub fn op_matches(assigned_skill: u32, receipt_op: u32) -> bool { + return (skill_op(assigned_skill) == receipt_op); +} + +pub fn result_binds_assign( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, +) -> bool { + if (is_hosted_skill(assigned_skill) == 1) { + if (assign_task_id == result_task_id) { + if (result_task_id == receipt_task) { + if (skill_family(assigned_skill) == receipt_family) { + return (skill_op(assigned_skill) == receipt_op); + } else { + return (receipt_family != receipt_family); + } + } else { + return (receipt_task != receipt_task); + } + } else { + return (assign_task_id != assign_task_id); + } + } else { + return (assigned_skill != assigned_skill); + } +} + +pub fn skill_width(skill: u32) -> u32 { + if (skill == SKILL_GFT8_MUL) { + return 8; + } + if (skill == SKILL_GFT8_ADD) { + return 8; + } + if (skill == SKILL_GFT4_MUL) { + return 4; + } + if (skill == SKILL_GFT4_ADD) { + return 4; + } + if (skill == SKILL_GFT32_MUL) { + return 32; + } + if (skill == SKILL_GFT32_ADD) { + return 32; + } + if (skill == SKILL_GFT64_MUL) { + return 64; + } + if (skill == SKILL_GFT64_ADD) { + return 64; + } + if (skill == SKILL_GFT128_MUL) { + return 128; + } + if (skill == SKILL_GFT128_ADD) { + return 128; + } + return 16; +} + +pub fn skill_et(skill: u32) -> u32 { + if (skill == SKILL_GFT16_MUL) { + return 4; + } + if (skill == SKILL_GFT16_ADD) { + return 4; + } + if (skill == SKILL_GFT8_MUL) { + return 3; + } + if (skill == SKILL_GFT8_ADD) { + return 3; + } + if (skill == SKILL_GFT4_MUL) { + return 2; + } + if (skill == SKILL_GFT4_ADD) { + return 2; + } + if (skill == SKILL_GFT32_MUL) { + return 6; + } + if (skill == SKILL_GFT32_ADD) { + return 6; + } + if (skill == SKILL_GFT64_MUL) { + return 9; + } + if (skill == SKILL_GFT64_ADD) { + return 9; + } + if (skill == SKILL_GFT128_MUL) { + return 14; + } + if (skill == SKILL_GFT128_ADD) { + return 14; + } + return 0; +} + +pub fn result_binds_assign_sized( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + receipt_width: u32, +) -> bool { + if (skill_width(assigned_skill) == receipt_width) { + return result_binds_assign( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + ); + } else { + return (receipt_width != receipt_width); + } +} + +pub fn result_binds_assign_rung( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + receipt_width: u32, + receipt_et: u32, +) -> bool { + if (skill_et(assigned_skill) == receipt_et) { + return result_binds_assign_sized( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + receipt_width, + ); + } else { + return (receipt_et != receipt_et); + } +} + +pub fn admit_result( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + last_settled: u32, + exec_rep: u32, + min_rep: u32, +) -> bool { + if (result_task_id > last_settled) { + if (exec_rep >= min_rep) { + return result_binds_assign( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + ); + } else { + return (exec_rep != exec_rep); + } + } else { + return (result_task_id != result_task_id); + } +} + +pub const A2A_BOND_BPS_UNIT: u32 = 10000; + +pub fn admit_result_bonded( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + last_settled: u32, + exec_rep: u32, + min_rep: u32, + bond: u32, + outstanding: u32, + min_bps: u32, +) -> bool { + let need: u64 = ((outstanding as u64) * (min_bps as u64)); + let required: u32 = ((need / (A2A_BOND_BPS_UNIT as u64)) as u32); + if (bond >= required) { + return admit_result( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + last_settled, + exec_rep, + min_rep, + ); + } else { + return (bond != bond); + } +} + +pub fn executor_binds_assign(assigned_executor: u32, receipt_executor: u32) -> bool { + return (assigned_executor == receipt_executor); +} + +pub fn admit_result_authentic( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + last_settled: u32, + exec_rep: u32, + min_rep: u32, + bond: u32, + outstanding: u32, + min_bps: u32, + assigned_executor: u32, + receipt_executor: u32, +) -> bool { + if (assigned_executor == receipt_executor) { + return admit_result_bonded( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + last_settled, + exec_rep, + min_rep, + bond, + outstanding, + min_bps, + ); + } else { + return (assigned_executor != assigned_executor); + } +} + +pub fn executor_authentic( + assigned_executor: u32, + receipt_executor: u32, + sig_ok: u32, + hashed_pubkey_lo: u32, +) -> bool { + if (assigned_executor == receipt_executor) { + if (sig_ok == 1) { + return (receipt_executor == hashed_pubkey_lo); + } else { + return (sig_ok != sig_ok); + } + } else { + return (assigned_executor != assigned_executor); + } +} + +pub fn admit_result_signed( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + last_settled: u32, + exec_rep: u32, + min_rep: u32, + bond: u32, + outstanding: u32, + min_bps: u32, + assigned_executor: u32, + receipt_executor: u32, + sig_ok: u32, + hashed_pubkey_lo: u32, +) -> bool { + if (assigned_executor == receipt_executor) { + if (sig_ok == 1) { + if (receipt_executor == hashed_pubkey_lo) { + return admit_result_bonded( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + last_settled, + exec_rep, + min_rep, + bond, + outstanding, + min_bps, + ); + } else { + return (receipt_executor != receipt_executor); + } + } else { + return (sig_ok != sig_ok); + } + } else { + return (assigned_executor != assigned_executor); + } +} + +pub fn admit_result_signed_sized( + assign_task_id: u32, + result_task_id: u32, + receipt_task: u32, + assigned_skill: u32, + receipt_family: u32, + receipt_op: u32, + last_settled: u32, + exec_rep: u32, + min_rep: u32, + bond: u32, + outstanding: u32, + min_bps: u32, + assigned_executor: u32, + receipt_executor: u32, + sig_ok: u32, + hashed_pubkey_lo: u32, + receipt_width: u32, +) -> bool { + if (skill_width(assigned_skill) == receipt_width) { + return admit_result_signed( + assign_task_id, + result_task_id, + receipt_task, + assigned_skill, + receipt_family, + receipt_op, + last_settled, + exec_rep, + min_rep, + bond, + outstanding, + min_bps, + assigned_executor, + receipt_executor, + sig_ok, + hashed_pubkey_lo, + ); + } else { + return (receipt_width != receipt_width); + } +} diff --git a/gen/rust/tri_compute_account.rs b/gen/rust/tri_compute_account.rs new file mode 100644 index 00000000..b6d6d755 --- /dev/null +++ b/gen/rust/tri_compute_account.rs @@ -0,0 +1,134 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub fn total(balance: u32, locked: u32) -> u32 { + return (balance + locked); +} + +pub fn bal_after_lock(balance: u32, amt: u32) -> u32 { + if (amt <= balance) { + return (balance - amt); + } else { + return balance; + } +} + +pub fn locked_after_lock(balance: u32, locked: u32, amt: u32) -> u32 { + if (amt <= balance) { + return (locked + amt); + } else { + return locked; + } +} + +pub fn bal_after_release(balance: u32, locked: u32) -> u32 { + return (balance + locked); +} + +pub fn bal_after_slash(balance: u32) -> u32 { + return balance; +} + +pub fn bal_add_sat(balance: u32, amount: u32) -> u32 { + let sum: u32 = (balance).wrapping_add(amount); + if (sum < balance) { + return 0xFFFFFFFF; + } else { + return sum; + } +} + +pub fn bal_after_settle(balance: u32, reward: u32) -> u32 { + return bal_add_sat(balance, reward); +} + +pub fn total3(balance: u32, locked: u32, pending: u32) -> u32 { + return ((balance + locked) + pending); +} + +pub fn pending_after_settle(pending: u32, reward: u32) -> u32 { + let sum: u32 = (pending).wrapping_add(reward); + if (sum < pending) { + return 0xFFFFFFFF; + } else { + return sum; + } +} + +pub fn bal_after_finalize(balance: u32, pending: u32) -> u32 { + return bal_add_sat(balance, pending); +} + +pub fn bal_after_clawback(balance: u32) -> u32 { + return balance; +} + +pub fn outstanding_after_escrow(outstanding: u32, reward: u32) -> u32 { + let sum: u32 = (outstanding).wrapping_add(reward); + if (sum < outstanding) { + return 0xFFFFFFFF; + } else { + return sum; + } +} + +pub fn outstanding_after_release(outstanding: u32, reward: u32) -> u32 { + if (reward <= outstanding) { + return (outstanding - reward); + } else { + return 0; + } +} + +pub fn pending_after_release(pending: u32, reward: u32) -> u32 { + if (reward <= pending) { + return (pending - reward); + } else { + return 0; + } +} + +pub fn escrow_consistent(pending: u32, outstanding: u32) -> bool { + return (pending == outstanding); +} + +pub fn is_final(settle_epoch: u32, now_epoch: u32, window: u32) -> bool { + if (now_epoch >= settle_epoch) { + return ((now_epoch - settle_epoch) >= window); + } else { + return false; + } +} + +pub fn bal_after_finalize_gated( + balance: u32, + pending: u32, + settle_epoch: u32, + now_epoch: u32, + window: u32, +) -> u32 { + if (now_epoch >= settle_epoch) { + if ((now_epoch - settle_epoch) >= window) { + return bal_add_sat(balance, pending); + } else { + return balance; + } + } else { + return balance; + } +} + +pub fn bal_after_finalize_checked( + balance: u32, + pending: u32, + settle_epoch: u32, + now_epoch: u32, + window: u32, + slashed: u32, +) -> u32 { + if (slashed == 1) { + return balance; + } else { + return bal_after_finalize_gated(balance, pending, settle_epoch, now_epoch, window); + } +} diff --git a/gen/rust/tri_compute_bitnet.rs b/gen/rust/tri_compute_bitnet.rs new file mode 100644 index 00000000..d2a0ece7 --- /dev/null +++ b/gen/rust/tri_compute_bitnet.rs @@ -0,0 +1,155 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const OP_BITNET: u32 = 0x20; + +pub const B_C: u32 = 0x85EBCA77; + +pub const TAG_BITNET: u32 = 0x54424E50; + +pub const SHA_PAD: u32 = 0x80000000; + +pub const BITNET_MSG_BITS: u32 = 224; + +pub fn rotl(x: u32, k: u32) -> u32 { + return ((x << k) | (x >> (32 - k))); +} + +pub fn mix32(x: u32) -> u32 { + let a: u32 = (x ^ (x >> 16)); + let b: u32 = (a).wrapping_add((a << 3)); + let c: u32 = (b ^ (b >> 11)); + let d: u32 = (c).wrapping_add((c << 15)); + return (d ^ (d >> 16)); +} + +pub fn is_active(trit: u32) -> u32 { + if (trit == 1) { + return 1; + } else { + if (trit == 2) { + return 1; + } else { + return 0; + } + } +} + +pub fn active_weights4(packed_w: u32) -> u32 { + return (((is_active((packed_w & 3)) + is_active(((packed_w >> 2) & 3))) + + is_active(((packed_w >> 4) & 3))) + + is_active(((packed_w >> 6) & 3))); +} + +pub fn trit_canonical(trit: u32) -> u32 { + if (trit == 3) { + return 0; + } else { + return 1; + } +} + +pub fn weights_canonical4(packed_w: u32) -> u32 { + return (((trit_canonical((packed_w & 3)) + trit_canonical(((packed_w >> 2) & 3))) + + trit_canonical(((packed_w >> 4) & 3))) + + trit_canonical(((packed_w >> 6) & 3))); +} + +pub fn packing_is_canonical(packed_w: u32) -> bool { + return (weights_canonical4(packed_w) == 4); +} + +pub fn is_pos(trit: u32) -> u32 { + if (trit == 1) { + return 1; + } else { + return 0; + } +} + +pub fn is_neg(trit: u32) -> u32 { + if (trit == 2) { + return 1; + } else { + return 0; + } +} + +pub fn pos_weights4(packed_w: u32) -> u32 { + return (((is_pos((packed_w & 3)) + is_pos(((packed_w >> 2) & 3))) + + is_pos(((packed_w >> 4) & 3))) + + is_pos(((packed_w >> 6) & 3))); +} + +pub fn neg_weights4(packed_w: u32) -> u32 { + return (((is_neg((packed_w & 3)) + is_neg(((packed_w >> 2) & 3))) + + is_neg(((packed_w >> 4) & 3))) + + is_neg(((packed_w >> 6) & 3))); +} + +pub fn sign_balance_biased(packed_w: u32) -> u32 { + return ((pos_weights4(packed_w) + 4) - neg_weights4(packed_w)); +} + +pub fn bitnet_balance_matches(weight_code: u32, claimed_balance_biased: u32) -> bool { + if (weights_canonical4(weight_code) == 4) { + return (sign_balance_biased(weight_code) == claimed_balance_biased); + } else { + return (weight_code != weight_code); + } +} + +pub fn bitnet_leaf( + weight_code: u32, + act_hash: u32, + gf_result: u32, + device: u32, + executor: u32, + epoch: u32, +) -> u32 { + let w: u32 = mix32((weight_code ^ rotl(OP_BITNET, 5))); + let a: u32 = mix32((w ^ rotl(act_hash, 11))); + let r: u32 = mix32((a ^ rotl(gf_result, 17))); + let d: u32 = mix32((r ^ rotl(device, 23))); + let e: u32 = mix32((d ^ rotl(executor, 29))); + return mix32((e ^ rotl(epoch, 13))); +} + +pub fn bitnet_digest_pre( + idx: u32, + weight_code: u32, + act_hash: u32, + gf_result: u32, + device: u32, + executor: u32, + epoch: u32, +) -> u32 { + if (idx == 0) { + return TAG_BITNET; + } + if (idx == 1) { + return weight_code; + } + if (idx == 2) { + return act_hash; + } + if (idx == 3) { + return gf_result; + } + if (idx == 4) { + return device; + } + if (idx == 5) { + return executor; + } + if (idx == 6) { + return epoch; + } + if (idx == 7) { + return SHA_PAD; + } + if (idx == 15) { + return BITNET_MSG_BITS; + } + return 0; +} diff --git a/gen/rust/tri_compute_bond.rs b/gen/rust/tri_compute_bond.rs new file mode 100644 index 00000000..572b99e9 --- /dev/null +++ b/gen/rust/tri_compute_bond.rs @@ -0,0 +1,86 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const ST_FREE: u32 = 0; + +pub const ST_LOCKED: u32 = 1; + +pub const ST_RELEASED: u32 = 2; + +pub const ST_SLASHED: u32 = 3; + +pub fn can_post(balance: u32, amount: u32) -> bool { + return (amount <= balance); +} + +pub fn balance_after_post(balance: u32, amount: u32) -> u32 { + if (amount <= balance) { + return (balance - amount); + } else { + return balance; + } +} + +pub fn locked_amount(balance: u32, amount: u32) -> u32 { + if (amount <= balance) { + return amount; + } else { + return 0; + } +} + +pub fn bond_state_after(outcome: u32) -> u32 { + if (outcome == 0) { + return ST_RELEASED; + } else { + if (outcome == 1) { + return ST_SLASHED; + } else { + return ST_LOCKED; + } + } +} + +pub fn balance_after_resolve(balance: u32, bond: u32, outcome: u32) -> u32 { + if (outcome == 0) { + let sum: u32 = (balance).wrapping_add(bond); + if (sum < balance) { + return 0xFFFFFFFF; + } else { + return sum; + } + } else { + return balance; + } +} + +pub const BOND_BPS_UNIT: u32 = 10000; + +pub fn required_bond(outstanding: u32, min_bps: u32) -> u32 { + let need: u64 = ((outstanding as u64) * (min_bps as u64)); + return ((need / (BOND_BPS_UNIT as u64)) as u32); +} + +pub fn bond_covers(bond: u32, outstanding: u32, min_bps: u32) -> bool { + return (bond >= required_bond(outstanding, min_bps)); +} + +pub const GFT16_ET: u32 = 4; + +pub const BOND_BPS_PER_TRIT: u32 = 500; + +pub fn rung_min_bps(min_bps: u32, gf_et: u32) -> u32 { + if (gf_et <= GFT16_ET) { + return min_bps; + } else { + return (min_bps + ((gf_et - GFT16_ET) * BOND_BPS_PER_TRIT)); + } +} + +pub fn required_bond_rung(outstanding: u32, min_bps: u32, gf_et: u32) -> u32 { + return required_bond(outstanding, rung_min_bps(min_bps, gf_et)); +} + +pub fn bond_covers_rung(bond: u32, outstanding: u32, min_bps: u32, gf_et: u32) -> bool { + return (bond >= required_bond_rung(outstanding, min_bps, gf_et)); +} diff --git a/gen/rust/tri_compute_challenge.rs b/gen/rust/tri_compute_challenge.rs new file mode 100644 index 00000000..80707c56 --- /dev/null +++ b/gen/rust/tri_compute_challenge.rs @@ -0,0 +1,649 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const RESOLVE_HONEST: u32 = 0; + +pub const RESOLVE_SLASH: u32 = 1; + +pub fn is_honest(claimed_result: u32, recomputed_result: u32) -> bool { + return (claimed_result == recomputed_result); +} + +pub fn resolve(claimed_result: u32, recomputed_result: u32) -> u32 { + if (claimed_result == recomputed_result) { + return RESOLVE_HONEST; + } else { + return RESOLVE_SLASH; + } +} + +pub fn executor_bond_after(prev_bond: u32, outcome: u32) -> u32 { + if (outcome == RESOLVE_SLASH) { + return 0; + } else { + return prev_bond; + } +} + +pub fn challenger_reward(bond: u32, outcome: u32) -> u32 { + if (outcome == RESOLVE_SLASH) { + return bond; + } else { + return 0; + } +} + +pub fn challenger_stake_after(stake: u32, outcome: u32) -> u32 { + if (outcome == RESOLVE_HONEST) { + return 0; + } else { + return stake; + } +} + +pub const RESOLVE_MALFORMED: u32 = 2; + +pub fn challenge_binds(settled_leaf: u32, dispute_leaf: u32) -> bool { + return (settled_leaf == dispute_leaf); +} + +pub fn resolve_bound( + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + recomputed_result: u32, +) -> u32 { + if (settled_leaf != dispute_leaf) { + return RESOLVE_MALFORMED; + } else { + if (claimed_result == recomputed_result) { + return RESOLVE_HONEST; + } else { + return RESOLVE_SLASH; + } + } +} + +pub fn challenger_stake_after_bound(stake: u32, outcome: u32) -> u32 { + if (outcome == RESOLVE_SLASH) { + return stake; + } else { + if (outcome == RESOLVE_STALE) { + return stake; + } else { + if (outcome == RESOLVE_INDETERMINATE) { + return stake; + } else { + return 0; + } + } + } +} + +pub const RESOLVE_STALE: u32 = 3; + +pub fn dispute_is_fresh(last_epoch: u32, dispute_epoch: u32) -> bool { + return (dispute_epoch > last_epoch); +} + +pub fn resolver_epoch_after(last_epoch: u32, dispute_epoch: u32, outcome: u32) -> u32 { + if (outcome == RESOLVE_HONEST) { + return dispute_epoch; + } else { + if (outcome == RESOLVE_SLASH) { + return dispute_epoch; + } else { + return last_epoch; + } + } +} + +pub const FMT_GF_BINARY: u32 = 0; + +pub const FMT_GFT: u32 = 1; + +pub const RESOLVE_FAMILY_MISMATCH: u32 = 4; + +pub fn family_matches(settled_family: u32, dispute_family: u32) -> bool { + return (settled_family == dispute_family); +} + +pub fn resolve_full( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + recomputed_result: u32, +) -> u32 { + if (dispute_epoch > last_epoch) { + if (settled_family == dispute_family) { + return resolve_bound( + settled_leaf, + dispute_leaf, + claimed_result, + recomputed_result, + ); + } else { + return RESOLVE_FAMILY_MISMATCH; + } + } else { + return RESOLVE_STALE; + } +} + +pub const RESOLVE_RUNG_MISMATCH: u32 = 6; + +pub fn rung_matches(settled_et: u32, dispute_et: u32) -> bool { + return (settled_et == dispute_et); +} + +pub fn resolve_full_rung( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_et: u32, + dispute_et: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + recomputed_result: u32, +) -> u32 { + if (dispute_epoch > last_epoch) { + if (settled_family == dispute_family) { + if (settled_et == dispute_et) { + return resolve_bound( + settled_leaf, + dispute_leaf, + claimed_result, + recomputed_result, + ); + } else { + return RESOLVE_RUNG_MISMATCH; + } + } else { + return RESOLVE_FAMILY_MISMATCH; + } + } else { + return RESOLVE_STALE; + } +} + +pub fn resolve_bound_d256(leaf_match: u32, claimed_result: u32, recomputed_result: u32) -> u32 { + if (leaf_match == 1) { + if (claimed_result == recomputed_result) { + return RESOLVE_HONEST; + } else { + return RESOLVE_SLASH; + } + } else { + return RESOLVE_MALFORMED; + } +} + +pub fn resolve_full_d256( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + leaf_match: u32, + claimed_result: u32, + recomputed_result: u32, +) -> u32 { + if (dispute_epoch > last_epoch) { + if (settled_family == dispute_family) { + return resolve_bound_d256(leaf_match, claimed_result, recomputed_result); + } else { + return RESOLVE_FAMILY_MISMATCH; + } + } else { + return RESOLVE_STALE; + } +} + +pub fn resolve_bitnet( + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + recomputed_result: u32, + ternary_ok: u32, +) -> u32 { + if (settled_leaf == dispute_leaf) { + if (ternary_ok == 1) { + return resolve_bound( + settled_leaf, + dispute_leaf, + claimed_result, + recomputed_result, + ); + } else { + return RESOLVE_SLASH; + } + } else { + return RESOLVE_MALFORMED; + } +} + +pub fn resolve_bitnet_full( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + recomputed_result: u32, + ternary_ok: u32, +) -> u32 { + if (dispute_epoch > last_epoch) { + if (settled_family == dispute_family) { + return resolve_bitnet( + settled_leaf, + dispute_leaf, + claimed_result, + recomputed_result, + ternary_ok, + ); + } else { + return RESOLVE_FAMILY_MISMATCH; + } + } else { + return RESOLVE_STALE; + } +} + +pub fn resolve_bitnet_d256( + leaf_match: u32, + claimed_result: u32, + recomputed_result: u32, + ternary_ok: u32, +) -> u32 { + if (leaf_match == 1) { + if (ternary_ok == 1) { + if (claimed_result == recomputed_result) { + return RESOLVE_HONEST; + } else { + return RESOLVE_SLASH; + } + } else { + return RESOLVE_SLASH; + } + } else { + return RESOLVE_MALFORMED; + } +} + +pub fn resolve_bitnet_d256_full( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + leaf_match: u32, + claimed_result: u32, + recomputed_result: u32, + ternary_ok: u32, +) -> u32 { + if (dispute_epoch > last_epoch) { + if (settled_family == dispute_family) { + return resolve_bitnet_d256(leaf_match, claimed_result, recomputed_result, ternary_ok); + } else { + return RESOLVE_FAMILY_MISMATCH; + } + } else { + return RESOLVE_STALE; + } +} + +pub fn majority_flag3(a: u32, b: u32, c: u32) -> u32 { + if (((a + b) + c) >= 2) { + return 1; + } else { + return 0; + } +} + +pub fn resolve_bitnet_quorum( + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, + t0: u32, + t1: u32, + t2: u32, +) -> u32 { + if (verifier_quorum3(v0, v1, v2) == 1) { + return resolve_bitnet( + settled_leaf, + dispute_leaf, + claimed_result, + quorum_result3(v0, v1, v2), + majority_flag3(t0, t1, t2), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub fn majority_flag5(a: u32, b: u32, c: u32, d: u32, e: u32) -> u32 { + if (((((a + b) + c) + d) + e) >= 3) { + return 1; + } else { + return 0; + } +} + +pub fn resolve_bitnet_quorum5( + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, + v3: u32, + v4: u32, + t0: u32, + t1: u32, + t2: u32, + t3: u32, + t4: u32, +) -> u32 { + if (has_quorum_k(max_agree5(v0, v1, v2, v3, v4), quorum_threshold(5)) == 1) { + return resolve_bitnet( + settled_leaf, + dispute_leaf, + claimed_result, + quorum_value5(v0, v1, v2, v3, v4, quorum_threshold(5)), + majority_flag5(t0, t1, t2, t3, t4), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub fn resolve_bitnet_quorum_full( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, + t0: u32, + t1: u32, + t2: u32, +) -> u32 { + if (verifier_quorum3(v0, v1, v2) == 1) { + return resolve_bitnet_full( + last_epoch, + dispute_epoch, + settled_family, + dispute_family, + settled_leaf, + dispute_leaf, + claimed_result, + quorum_result3(v0, v1, v2), + majority_flag3(t0, t1, t2), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub fn resolve_bitnet_quorum5_full( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, + v3: u32, + v4: u32, + t0: u32, + t1: u32, + t2: u32, + t3: u32, + t4: u32, +) -> u32 { + if (has_quorum_k(max_agree5(v0, v1, v2, v3, v4), quorum_threshold(5)) == 1) { + return resolve_bitnet_full( + last_epoch, + dispute_epoch, + settled_family, + dispute_family, + settled_leaf, + dispute_leaf, + claimed_result, + quorum_value5(v0, v1, v2, v3, v4, quorum_threshold(5)), + majority_flag5(t0, t1, t2, t3, t4), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub const RESOLVE_INDETERMINATE: u32 = 5; + +pub fn verifier_quorum3(v0: u32, v1: u32, v2: u32) -> u32 { + if (v0 == v1) { + return 1; + } + if (v0 == v2) { + return 1; + } + if (v1 == v2) { + return 1; + } + return 0; +} + +pub fn quorum_result3(v0: u32, v1: u32, v2: u32) -> u32 { + if (v0 == v1) { + return v0; + } + if (v0 == v2) { + return v0; + } + if (v1 == v2) { + return v1; + } + return v0; +} + +pub fn resolve_quorum3( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, +) -> u32 { + if (verifier_quorum3(v0, v1, v2) == 1) { + return resolve_full( + last_epoch, + dispute_epoch, + settled_family, + dispute_family, + settled_leaf, + dispute_leaf, + claimed_result, + quorum_result3(v0, v1, v2), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub fn verifier_dissented(vote: u32, quorum_value: u32, has_quorum: u32) -> u32 { + if (has_quorum == 1) { + if (vote == quorum_value) { + return 0; + } else { + return 1; + } + } else { + return 0; + } +} + +pub fn verifier_stake_after(stake: u32, dissented: u32) -> u32 { + if (dissented == 1) { + return 0; + } else { + return stake; + } +} + +pub fn verifier_reward(burned_total: u32, honest_count: u32) -> u32 { + if (honest_count == 0) { + return 0; + } else { + let share: u64 = ((burned_total as u64) / (honest_count as u64)); + return (share as u32); + } +} + +pub fn quorum_threshold(n: u32) -> u32 { + return ((n >> 1) + 1); +} + +pub fn has_quorum_k(agree_count: u32, k: u32) -> u32 { + if (agree_count >= k) { + return 1; + } else { + return 0; + } +} + +pub fn max_agree3(v0: u32, v1: u32, v2: u32) -> u32 { + if (v0 == v1) { + if (v1 == v2) { + return 3; + } else { + return 2; + } + } else { + if (v0 == v2) { + return 2; + } else { + if (v1 == v2) { + return 2; + } else { + return 1; + } + } + } +} + +pub fn eq1(a: u32, b: u32) -> u32 { + if (a == b) { + return 1; + } else { + return 0; + } +} + +pub fn max2(a: u32, b: u32) -> u32 { + if (a >= b) { + return a; + } else { + return b; + } +} + +pub fn count5(x: u32, v0: u32, v1: u32, v2: u32, v3: u32, v4: u32) -> u32 { + return ((((eq1(x, v0) + eq1(x, v1)) + eq1(x, v2)) + eq1(x, v3)) + eq1(x, v4)); +} + +pub fn max_agree5(v0: u32, v1: u32, v2: u32, v3: u32, v4: u32) -> u32 { + let c0: u32 = count5(v0, v0, v1, v2, v3, v4); + let c1: u32 = count5(v1, v0, v1, v2, v3, v4); + let c2: u32 = count5(v2, v0, v1, v2, v3, v4); + let c3: u32 = count5(v3, v0, v1, v2, v3, v4); + let c4: u32 = count5(v4, v0, v1, v2, v3, v4); + return max2(max2(max2(c0, c1), max2(c2, c3)), c4); +} + +pub fn quorum_value5(v0: u32, v1: u32, v2: u32, v3: u32, v4: u32, k: u32) -> u32 { + if (count5(v0, v0, v1, v2, v3, v4) >= k) { + return v0; + } + if (count5(v1, v0, v1, v2, v3, v4) >= k) { + return v1; + } + if (count5(v2, v0, v1, v2, v3, v4) >= k) { + return v2; + } + if (count5(v3, v0, v1, v2, v3, v4) >= k) { + return v3; + } + if (count5(v4, v0, v1, v2, v3, v4) >= k) { + return v4; + } + return v0; +} + +pub fn resolve_quorum5( + last_epoch: u32, + dispute_epoch: u32, + settled_family: u32, + dispute_family: u32, + settled_leaf: u32, + dispute_leaf: u32, + claimed_result: u32, + v0: u32, + v1: u32, + v2: u32, + v3: u32, + v4: u32, +) -> u32 { + if (has_quorum_k(max_agree5(v0, v1, v2, v3, v4), quorum_threshold(5)) == 1) { + return resolve_full( + last_epoch, + dispute_epoch, + settled_family, + dispute_family, + settled_leaf, + dispute_leaf, + claimed_result, + quorum_value5(v0, v1, v2, v3, v4, quorum_threshold(5)), + ); + } else { + return RESOLVE_INDETERMINATE; + } +} + +pub fn dissenter_count5(v0: u32, v1: u32, v2: u32, v3: u32, v4: u32) -> u32 { + return (5 - max_agree5(v0, v1, v2, v3, v4)); +} + +pub fn burned_total5(v0: u32, v1: u32, v2: u32, v3: u32, v4: u32, stake: u32) -> u32 { + let prod: u64 = ((dissenter_count5(v0, v1, v2, v3, v4) as u64) * (stake as u64)); + if (prod > 4294967295) { + return 4294967295; + } else { + return (prod as u32); + } +} + +pub fn honest_share5(v0: u32, v1: u32, v2: u32, v3: u32, v4: u32, stake: u32) -> u32 { + return verifier_reward( + burned_total5(v0, v1, v2, v3, v4, stake), + max_agree5(v0, v1, v2, v3, v4), + ); +} diff --git a/gen/rust/tri_compute_gfvalid.rs b/gen/rust/tri_compute_gfvalid.rs new file mode 100644 index 00000000..97405548 --- /dev/null +++ b/gen/rust/tri_compute_gfvalid.rs @@ -0,0 +1,125 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub fn is_finite_gf(result: u32, exp_bits: u32, mant_bits: u32) -> bool { + let exp_mask: u32 = ((1 << exp_bits) - 1); + let exp: u32 = ((result >> mant_bits) & exp_mask); + return (exp != exp_mask); +} + +pub fn is_finite_gf_h(result: u32, exp_bits: u32, mant_bits: u32, has_inf: u32) -> bool { + let exp_mask: u32 = ((1 << exp_bits) - 1); + let exp: u32 = ((result >> mant_bits) & exp_mask); + if (has_inf == 1) { + return (exp != exp_mask); + } else { + return (exp == exp); + } +} + +pub const FMT_GF_BINARY: u32 = 0; + +pub const FMT_GFT16: u32 = 1; + +pub fn gft_pow3(exp_trits: u32) -> u32 { + if (exp_trits == 2) { + return 9; + } + if (exp_trits == 3) { + return 27; + } + if (exp_trits == 4) { + return 81; + } + if (exp_trits == 5) { + return 243; + } + if (exp_trits == 6) { + return 729; + } + if (exp_trits == 7) { + return 2187; + } + if (exp_trits == 8) { + return 6561; + } + if (exp_trits == 9) { + return 19683; + } + if (exp_trits == 14) { + return 4782969; + } + return 0; +} + +pub fn gft_offset_max(exp_trits: u32) -> u32 { + return (gft_pow3(exp_trits) - 1); +} + +pub fn gft_exp_trits_for_width(width: u32) -> u32 { + if (width == 4) { + return 2; + } + if (width == 8) { + return 3; + } + if (width == 16) { + return 4; + } + if (width == 32) { + return 6; + } + if (width == 64) { + return 9; + } + if (width == 128) { + return 14; + } + return 0; +} + +pub fn gft_offset_max_for_width(width: u32) -> u32 { + let et: u32 = gft_exp_trits_for_width(width); + if (et == 0) { + return 0; + } else { + return gft_offset_max(et); + } +} + +pub fn is_finite_gft_n(offset: u32, exp_trits: u32) -> bool { + return (offset != gft_offset_max(exp_trits)); +} + +pub fn gft_offset_in_range(offset: u32, exp_trits: u32) -> bool { + return (offset < gft_pow3(exp_trits)); +} + +pub fn is_valid_gft(offset: u32, exp_trits: u32) -> bool { + if (offset < gft_pow3(exp_trits)) { + return (offset != gft_offset_max(exp_trits)); + } else { + return (offset != offset); + } +} + +pub const GFT16_OFFSET_MAX: u32 = 80; + +pub fn is_finite_gft16(offset: u32) -> bool { + return is_finite_gft_n(offset, 4); +} + +pub fn is_finite_dispatch( + fmt_family: u32, + value: u32, + exp_bits: u32, + mant_bits: u32, + has_inf: u32, + exp_trits: u32, +) -> bool { + if (fmt_family == FMT_GFT16) { + return is_finite_gft_n(value, exp_trits); + } else { + return is_finite_gf_h(value, exp_bits, mant_bits, has_inf); + } +} diff --git a/gen/rust/tri_compute_pool.rs b/gen/rust/tri_compute_pool.rs new file mode 100644 index 00000000..f1259fcc --- /dev/null +++ b/gen/rust/tri_compute_pool.rs @@ -0,0 +1,56 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub fn total_work3(w0: u32, w1: u32, w2: u32) -> u32 { + let sum: u64 = (((w0 as u64) + (w1 as u64)) + (w2 as u64)); + if (sum > 4294967295) { + return 4294967295; + } else { + return (sum as u32); + } +} + +pub fn pool_share(total_pool: u32, my_work: u32, total: u32) -> u32 { + if (total == 0) { + return 0; + } else { + let num: u64 = ((total_pool as u64) * (my_work as u64)); + let den: u64 = (total as u64); + return ((num / den) as u32); + } +} + +pub fn pool_after_deposit(pool: u32, amount: u32) -> u32 { + let sum: u32 = (pool).wrapping_add(amount); + if (sum < pool) { + return 0xFFFFFFFF; + } else { + return sum; + } +} + +pub fn payout_capped(pool: u32, requested: u32) -> u32 { + if (requested <= pool) { + return requested; + } else { + return pool; + } +} + +pub fn pool_after_payout(pool: u32, requested: u32) -> u32 { + if (requested <= pool) { + return (pool - requested); + } else { + return 0; + } +} + +pub fn balance_after_pool_settle(prev_balance: u32, pool: u32, reward: u32) -> u32 { + let credit: u32 = payout_capped(pool, reward); + let sum: u32 = (prev_balance).wrapping_add(credit); + if (sum < prev_balance) { + return 0xFFFFFFFF; + } else { + return sum; + } +} diff --git a/gen/rust/tri_compute_reputation.rs b/gen/rust/tri_compute_reputation.rs new file mode 100644 index 00000000..ce3ac814 --- /dev/null +++ b/gen/rust/tri_compute_reputation.rs @@ -0,0 +1,76 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const REP_INIT: u32 = 100; + +pub const REP_MAX: u32 = 1000; + +pub const RESOLVE_HONEST: u32 = 0; + +pub const RESOLVE_SLASH: u32 = 1; + +pub fn rep_after_honest(rep: u32, gain: u32) -> u32 { + let r: u64 = ((rep as u64) + (gain as u64)); + if (r > (REP_MAX as u64)) { + return REP_MAX; + } else { + return (r as u32); + } +} + +pub fn rep_after_slash(rep: u32) -> u32 { + return (rep >> 1); +} + +pub fn rep_after_resolution(rep: u32, outcome: u32, gain: u32) -> u32 { + if (outcome == RESOLVE_SLASH) { + return rep_after_slash(rep); + } else { + if (outcome == RESOLVE_HONEST) { + return rep_after_honest(rep, gain); + } else { + return rep; + } + } +} + +pub const GFT16_ET: u32 = 4; + +pub const REP_GAIN_PER_TRIT: u32 = 3; + +pub fn rung_honest_gain(base_gain: u32, gf_et: u32) -> u32 { + if (gf_et <= GFT16_ET) { + return base_gain; + } else { + return (base_gain + ((gf_et - GFT16_ET) * REP_GAIN_PER_TRIT)); + } +} + +pub fn rep_after_resolution_rung(rep: u32, outcome: u32, base_gain: u32, gf_et: u32) -> u32 { + return rep_after_resolution(rep, outcome, rung_honest_gain(base_gain, gf_et)); +} + +pub fn rep_after_verifier(rep: u32, has_quorum: u32, dissented: u32, gain: u32) -> u32 { + if (has_quorum == 1) { + if (dissented == 1) { + return rep_after_slash(rep); + } else { + return rep_after_honest(rep, gain); + } + } else { + return rep; + } +} + +pub fn weighted_work(raw_work: u32, rep: u32) -> u32 { + let prod: u64 = ((raw_work as u64) * (rep as u64)); + if (prod > 4294967295) { + return 4294967295; + } else { + return (prod as u32); + } +} + +pub fn can_admit(rep: u32, min_rep: u32) -> bool { + return (rep >= min_rep); +} diff --git a/gen/rust/tri_compute_safety.rs b/gen/rust/tri_compute_safety.rs new file mode 100644 index 00000000..d4e418f2 --- /dev/null +++ b/gen/rust/tri_compute_safety.rs @@ -0,0 +1,48 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub fn payable(fresh: u32, finite: u32, already_settled: u32) -> u32 { + if (already_settled == 1) { + return 0; + } else { + if (fresh == 1) { + if (finite == 1) { + return 1; + } else { + return 0; + } + } else { + return 0; + } + } +} + +pub fn reward_gate(base_reward: u32, fresh: u32, finite: u32, already_settled: u32) -> u32 { + if (payable(fresh, finite, already_settled) == 1) { + return base_reward; + } else { + return 0; + } +} + +pub fn payable_authentic(sig_ok: u32, fresh: u32, finite: u32, already_settled: u32) -> u32 { + if (sig_ok == 1) { + return payable(fresh, finite, already_settled); + } else { + return 0; + } +} + +pub fn reward_gate_authentic( + base_reward: u32, + sig_ok: u32, + fresh: u32, + finite: u32, + already_settled: u32, +) -> u32 { + if (payable_authentic(sig_ok, fresh, finite, already_settled) == 1) { + return base_reward; + } else { + return 0; + } +} diff --git a/gen/rust/tri_compute_settle.rs b/gen/rust/tri_compute_settle.rs new file mode 100644 index 00000000..a60dd330 --- /dev/null +++ b/gen/rust/tri_compute_settle.rs @@ -0,0 +1,327 @@ +// Generated from .t27 spec +// DO NOT EDIT — generated by t27c + +pub const SETTLE_GENESIS: u32 = 0x54524353; + +pub const S_C: u32 = 0x9E3779B9; + +pub const REWARD_PER_GF_OP: u32 = 1; + +pub const WORK_BPS_UNIT: u32 = 10000; + +pub fn rotl(x: u32, k: u32) -> u32 { + return ((x << k) | (x >> (32 - k))); +} + +pub fn mix32(x: u32) -> u32 { + let a: u32 = (x ^ (x >> 16)); + let b: u32 = (a).wrapping_add((a << 3)); + let c: u32 = (b ^ (b >> 11)); + let d: u32 = (c).wrapping_add((c << 15)); + return (d ^ (d >> 16)); +} + +pub fn balance_add(bal: u32, reward: u32) -> u32 { + let sum: u32 = (bal).wrapping_add(reward); + if (sum < bal) { + return 0xFFFFFFFF; + } else { + return sum; + } +} + +pub fn compute_reward(gf_width: u32, fresh: u32) -> u32 { + if (fresh == 1) { + return (gf_width * REWARD_PER_GF_OP); + } else { + return 0; + } +} + +pub fn compute_reward_fmt(gf_width: u32, fresh: u32, work_bps: u32) -> u32 { + if (fresh == 1) { + let scaled: u64 = ((gf_width as u64) * (work_bps as u64)); + return ((scaled / (WORK_BPS_UNIT as u64)) as u32); + } else { + return 0; + } +} + +pub fn settle_balance(prev_balance: u32, gf_width: u32, fresh: u32) -> u32 { + return balance_add(prev_balance, compute_reward(gf_width, fresh)); +} + +pub fn settle_head(prev_head: u32, digest: u32) -> u32 { + return mix32((prev_head ^ mix32((digest ^ S_C)))); +} + +pub fn is_finite_gf16(gf_result: u32) -> bool { + let exp: u32 = ((gf_result >> 9) & 0x3F); + return (exp != 0x3F); +} + +pub const FMT_GF_BINARY: u32 = 0; + +pub const FMT_GFT: u32 = 1; + +pub fn payable_flag( + fmt_family: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + has_inf: u32, + offset_max: u32, +) -> u32 { + let exp_mask: u32 = ((1 << exp_bits) - 1); + let exp: u32 = ((gf_result >> mant_bits) & exp_mask); + if (fmt_family == FMT_GFT) { + if (gf_result < offset_max) { + return 1; + } else { + return 0; + } + } else { + if (has_inf == 1) { + if (exp == exp_mask) { + return 0; + } else { + return 1; + } + } else { + return 1; + } + } +} + +pub fn settle_canonical( + prev_balance: u32, + gf_width: u32, + sig_ok: u32, + fresh: u32, + already_settled: u32, + fmt_family: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + has_inf: u32, + offset_max: u32, +) -> u32 { + let payable: u32 = payable_flag( + fmt_family, gf_result, exp_bits, mant_bits, has_inf, offset_max, + ); + if (sig_ok == 1) { + if (already_settled == 0) { + if (fresh == 1) { + return balance_add(prev_balance, compute_reward(gf_width, payable)); + } else { + return balance_add(prev_balance, compute_reward(gf_width, 0)); + } + } else { + return balance_add(prev_balance, compute_reward(gf_width, 0)); + } + } else { + return balance_add(prev_balance, compute_reward(gf_width, 0)); + } +} + +pub fn settle_canonical_fmt( + prev_balance: u32, + gf_width: u32, + sig_ok: u32, + fresh: u32, + already_settled: u32, + fmt_family: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + has_inf: u32, + offset_max: u32, + work_bps: u32, +) -> u32 { + let payable: u32 = payable_flag( + fmt_family, gf_result, exp_bits, mant_bits, has_inf, offset_max, + ); + if (sig_ok == 1) { + if (already_settled == 0) { + if (fresh == 1) { + return balance_add( + prev_balance, + compute_reward_fmt(gf_width, payable, work_bps), + ); + } else { + return balance_add(prev_balance, compute_reward_fmt(gf_width, 0, work_bps)); + } + } else { + return balance_add(prev_balance, compute_reward_fmt(gf_width, 0, work_bps)); + } + } else { + return balance_add(prev_balance, compute_reward_fmt(gf_width, 0, work_bps)); + } +} + +pub fn settle_checked(prev_balance: u32, gf_width: u32, fresh: u32, gf_result: u32) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + 1, + fresh, + 0, + FMT_GF_BINARY, + gf_result, + 6, + 9, + 1, + 0, + ); +} + +pub fn settle_checked_gf( + prev_balance: u32, + gf_width: u32, + fresh: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, +) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + 1, + fresh, + 0, + FMT_GF_BINARY, + gf_result, + exp_bits, + mant_bits, + 1, + 0, + ); +} + +pub fn settle_checked_gft( + prev_balance: u32, + gf_width: u32, + fresh: u32, + offset: u32, + offset_max: u32, +) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + 1, + fresh, + 0, + FMT_GFT, + offset, + 0, + 0, + 0, + offset_max, + ); +} + +pub fn gft_offset_max_w(gf_width: u32) -> u32 { + if (gf_width == 4) { + return 8; + } + if (gf_width == 8) { + return 26; + } + if (gf_width == 16) { + return 80; + } + if (gf_width == 32) { + return 728; + } + if (gf_width == 64) { + return 19682; + } + if (gf_width == 128) { + return 4782968; + } + return 0; +} + +pub fn settle_checked_gft_w(prev_balance: u32, gf_width: u32, fresh: u32, offset: u32) -> u32 { + return settle_checked_gft( + prev_balance, + gf_width, + fresh, + offset, + gft_offset_max_w(gf_width), + ); +} + +pub fn settle_full( + prev_balance: u32, + gf_width: u32, + fresh: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + already_settled: u32, +) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + 1, + fresh, + already_settled, + FMT_GF_BINARY, + gf_result, + exp_bits, + mant_bits, + 1, + 0, + ); +} + +pub fn settle_full_h( + prev_balance: u32, + gf_width: u32, + fresh: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + has_inf: u32, + already_settled: u32, +) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + 1, + fresh, + already_settled, + FMT_GF_BINARY, + gf_result, + exp_bits, + mant_bits, + has_inf, + 0, + ); +} + +pub fn settle_signed( + prev_balance: u32, + gf_width: u32, + fresh: u32, + gf_result: u32, + exp_bits: u32, + mant_bits: u32, + already_settled: u32, + sig_ok: u32, +) -> u32 { + return settle_canonical( + prev_balance, + gf_width, + sig_ok, + fresh, + already_settled, + FMT_GF_BINARY, + gf_result, + exp_bits, + mant_bits, + 1, + 0, + ); +} diff --git a/tests/compute_ring_invariants.rs b/tests/compute_ring_invariants.rs index d79798c3..a0bb1526 100644 --- a/tests/compute_ring_invariants.rs +++ b/tests/compute_ring_invariants.rs @@ -11,34 +11,34 @@ //! The generated modules are self-contained, so we include them directly by path //! rather than through the lib (which does not re-export the compute ring). -#[allow(dead_code, unused_parens)] -#[path = "../gen/rust/tri_compute_challenge.rs"] -mod challenge; -#[allow(dead_code, unused_parens)] +#[allow(clippy::all, unused)] +#[path = "../gen/rust/tri_a2a.rs"] +mod a2a; +#[allow(clippy::all, unused)] #[path = "../gen/rust/tri_compute_account.rs"] mod account; -#[allow(dead_code, unused_parens)] -#[path = "../gen/rust/tri_compute_reputation.rs"] -mod reputation; -#[allow(dead_code, unused_parens)] -#[path = "../gen/rust/tri_compute_pool.rs"] -mod pool; -#[allow(dead_code, unused_parens)] +#[allow(clippy::all, unused)] +#[path = "../gen/rust/tri_compute_bitnet.rs"] +mod bitnet; +#[allow(clippy::all, unused)] #[path = "../gen/rust/tri_compute_bond.rs"] mod bond; -#[allow(dead_code, unused_parens)] -#[path = "../gen/rust/tri_a2a.rs"] -mod a2a; -#[allow(dead_code, unused_parens)] +#[allow(clippy::all, unused)] +#[path = "../gen/rust/tri_compute_challenge.rs"] +mod challenge; +#[allow(clippy::all, unused)] #[path = "../gen/rust/tri_compute_gfvalid.rs"] mod gfvalid; -#[allow(dead_code, unused_parens)] +#[allow(clippy::all, unused)] +#[path = "../gen/rust/tri_compute_pool.rs"] +mod pool; +#[allow(clippy::all, unused)] +#[path = "../gen/rust/tri_compute_reputation.rs"] +mod reputation; +#[allow(clippy::all, unused)] #[path = "../gen/rust/tri_compute_safety.rs"] mod safety; -#[allow(dead_code, unused_parens)] -#[path = "../gen/rust/tri_compute_bitnet.rs"] -mod bitnet; -#[allow(dead_code, unused_parens)] +#[allow(clippy::all, unused)] #[path = "../gen/rust/tri_compute_settle.rs"] mod settle; @@ -47,10 +47,18 @@ mod settle; #[test] fn burned_total5_saturates_not_wraps() { use challenge::*; - assert_eq!(burned_total5(0x41, 0x42, 0x43, 0x44, 0x45, 100), 400, "small stake exact"); + assert_eq!( + burned_total5(0x41, 0x42, 0x43, 0x44, 0x45, 100), + 400, + "small stake exact" + ); // 4 * 1.1e9 = 4.4e9 overflows u32; a bare multiply would wrap to 105_032_704. assert_eq!(4_400_000_000u64 as u32, 105_032_704); - assert_eq!(burned_total5(0x41, 0x42, 0x43, 0x44, 0x45, 1_100_000_000), 4_294_967_295, "saturates"); + assert_eq!( + burned_total5(0x41, 0x42, 0x43, 0x44, 0x45, 1_100_000_000), + 4_294_967_295, + "saturates" + ); } #[test] @@ -58,10 +66,26 @@ fn credit_adds_saturate_not_wrap() { // account mint, pool-settle credit, and bond release all cap at u32 max. assert_eq!(account::bal_add_sat(0xFFFF_FFF0, 200), 0xFFFF_FFFF); assert_eq!(account::bal_add_sat(500, 200), 700); - assert_eq!(pool::balance_after_pool_settle(0xFFFF_FFF0, 1000, 300), 0xFFFF_FFFF, "pool credit saturates"); - assert_eq!(pool::balance_after_pool_settle(500, 1000, 300), 800, "normal pool credit exact"); - assert_eq!(bond::balance_after_resolve(0xFFFF_FFF0, 200, 0), 0xFFFF_FFFF, "honest bond release saturates"); - assert_eq!(bond::balance_after_resolve(0xFFFF_FFF0, 200, 1), 0xFFFF_FFF0, "slash never adds"); + assert_eq!( + pool::balance_after_pool_settle(0xFFFF_FFF0, 1000, 300), + 0xFFFF_FFFF, + "pool credit saturates" + ); + assert_eq!( + pool::balance_after_pool_settle(500, 1000, 300), + 800, + "normal pool credit exact" + ); + assert_eq!( + bond::balance_after_resolve(0xFFFF_FFF0, 200, 0), + 0xFFFF_FFFF, + "honest bond release saturates" + ); + assert_eq!( + bond::balance_after_resolve(0xFFFF_FFF0, 200, 1), + 0xFFFF_FFF0, + "slash never adds" + ); } #[test] @@ -70,7 +94,11 @@ fn reputation_cap_survives_overflowing_gain() { assert_eq!(rep_after_honest(100, 20), 120, "normal gain exact"); assert_eq!(rep_after_honest(990, 50), REP_MAX, "finite-large gain caps"); // rep=1000 + gain=0xFFFFFC18 wraps a u32 sum to exactly 0; the cap must still hold. - assert_eq!(rep_after_honest(1000, 4_294_966_296), REP_MAX, "overflowing gain caps, not zeroes"); + assert_eq!( + rep_after_honest(1000, 4_294_966_296), + REP_MAX, + "overflowing gain caps, not zeroes" + ); } // ---- dispute anti-replay: every resolver family member is STALE on replay ---- @@ -79,31 +107,77 @@ fn reputation_cap_survives_overflowing_gain() { fn every_resolver_is_stale_on_replay() { use challenge::*; let (gft, bin) = (FMT_GFT, FMT_GF_BINARY); - assert_eq!(resolve_full(5, 5, bin, bin, 0xAB, 0xAB, 0x9999, 0x4100), RESOLVE_STALE); - assert_eq!(resolve_full_d256(5, 5, bin, bin, 1, 0x9999, 0x4100), RESOLVE_STALE); - assert_eq!(resolve_bitnet_full(6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 1), RESOLVE_STALE); - assert_eq!(resolve_bitnet_quorum_full(6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 0x4100, 0x9999, 1, 1, 1), RESOLVE_STALE); - assert_eq!(resolve_bitnet_quorum5_full(6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 0x4100, 0x4100, 0x9999, 0x9999, 1, 1, 1, 1, 1), RESOLVE_STALE); - assert_eq!(resolve_bitnet_d256_full(6, 6, gft, gft, 1, 0x9999, 0x4100, 1), RESOLVE_STALE); + assert_eq!( + resolve_full(5, 5, bin, bin, 0xAB, 0xAB, 0x9999, 0x4100), + RESOLVE_STALE + ); + assert_eq!( + resolve_full_d256(5, 5, bin, bin, 1, 0x9999, 0x4100), + RESOLVE_STALE + ); + assert_eq!( + resolve_bitnet_full(6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 1), + RESOLVE_STALE + ); + assert_eq!( + resolve_bitnet_quorum_full( + 6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 0x4100, 0x9999, 1, 1, 1 + ), + RESOLVE_STALE + ); + assert_eq!( + resolve_bitnet_quorum5_full( + 6, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 0x4100, 0x4100, 0x9999, 0x9999, 1, 1, 1, 1, + 1 + ), + RESOLVE_STALE + ); + assert_eq!( + resolve_bitnet_d256_full(6, 6, gft, gft, 1, 0x9999, 0x4100, 1), + RESOLVE_STALE + ); } #[test] fn every_resolver_is_family_mismatch_cross_family() { use challenge::*; let (gft, bin) = (FMT_GFT, FMT_GF_BINARY); - assert_eq!(resolve_full(0, 6, bin, gft, 0xAB, 0xAB, 0x9999, 0x4100), RESOLVE_FAMILY_MISMATCH); - assert_eq!(resolve_bitnet_full(0, 6, gft, bin, 0xAB, 0xAB, 0x4100, 0x4100, 1), RESOLVE_FAMILY_MISMATCH); - assert_eq!(resolve_bitnet_quorum_full(0, 6, gft, bin, 0xAB, 0xAB, 0x4100, 0x4100, 0x4100, 0x4100, 1, 1, 1), RESOLVE_FAMILY_MISMATCH); - assert_eq!(resolve_bitnet_d256_full(0, 6, gft, bin, 1, 0x4100, 0x4100, 1), RESOLVE_FAMILY_MISMATCH); + assert_eq!( + resolve_full(0, 6, bin, gft, 0xAB, 0xAB, 0x9999, 0x4100), + RESOLVE_FAMILY_MISMATCH + ); + assert_eq!( + resolve_bitnet_full(0, 6, gft, bin, 0xAB, 0xAB, 0x4100, 0x4100, 1), + RESOLVE_FAMILY_MISMATCH + ); + assert_eq!( + resolve_bitnet_quorum_full( + 0, 6, gft, bin, 0xAB, 0xAB, 0x4100, 0x4100, 0x4100, 0x4100, 1, 1, 1 + ), + RESOLVE_FAMILY_MISMATCH + ); + assert_eq!( + resolve_bitnet_d256_full(0, 6, gft, bin, 1, 0x4100, 0x4100, 1), + RESOLVE_FAMILY_MISMATCH + ); } #[test] fn fresh_fraud_still_slashes() { use challenge::*; let (gft, bin) = (FMT_GFT, FMT_GF_BINARY); - assert_eq!(resolve_full(0, 6, bin, bin, 0xAB, 0xAB, 0x9999, 0x4100), RESOLVE_SLASH); - assert_eq!(resolve_bitnet_full(0, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 1), RESOLVE_SLASH); - assert_eq!(resolve_bitnet_d256_full(0, 6, gft, gft, 1, 0x9999, 0x4100, 1), RESOLVE_SLASH); + assert_eq!( + resolve_full(0, 6, bin, bin, 0xAB, 0xAB, 0x9999, 0x4100), + RESOLVE_SLASH + ); + assert_eq!( + resolve_bitnet_full(0, 6, gft, gft, 0xAB, 0xAB, 0x9999, 0x4100, 1), + RESOLVE_SLASH + ); + assert_eq!( + resolve_bitnet_d256_full(0, 6, gft, gft, 1, 0x9999, 0x4100, 1), + RESOLVE_SLASH + ); } // ---- escrow / finality ---- @@ -111,9 +185,21 @@ fn fresh_fraud_still_slashes() { #[test] fn slashed_reward_never_finalizes() { use account::*; - assert_eq!(bal_after_finalize_checked(500, 16, 0, 100, 10, 0), 516, "clean reward finalizes after window"); - assert_eq!(bal_after_finalize_checked(500, 16, 0, 100, 10, 1), 500, "slashed never finalizes"); - assert_eq!(bal_after_finalize_checked(500, 16, 0, 999_999, 10, 1), 500, "slashed blocked no matter the clock"); + assert_eq!( + bal_after_finalize_checked(500, 16, 0, 100, 10, 0), + 516, + "clean reward finalizes after window" + ); + assert_eq!( + bal_after_finalize_checked(500, 16, 0, 100, 10, 1), + 500, + "slashed never finalizes" + ); + assert_eq!( + bal_after_finalize_checked(500, 16, 0, 999_999, 10, 1), + 500, + "slashed blocked no matter the clock" + ); } #[test] @@ -121,8 +207,16 @@ fn clawback_removes_reward_from_pending() { use account::*; let pend0 = pending_after_settle(0, 16); assert_eq!(pend0, 16); - assert_eq!(pending_after_release(pend0, 16), 0, "clawback empties pending"); - assert_eq!(pending_after_release(10, 20), 0, "over-release floors, no underflow"); + assert_eq!( + pending_after_release(pend0, 16), + 0, + "clawback empties pending" + ); + assert_eq!( + pending_after_release(10, 20), + 0, + "over-release floors, no underflow" + ); } // ---- A2A authenticated ingress ---- @@ -131,11 +225,111 @@ fn clawback_removes_reward_from_pending() { fn admit_result_signed_rejects_forge_and_frontrun() { use a2a::*; let (task, wm, exe) = (0x777u32, 0x100u32, 0xE0E0u32); - assert!(admit_result_signed(task, task, task, SKILL_GF16_MUL, 0, 0x11, wm, 100, 50, 2000, 10000, 2000, exe, exe, 1, exe), "honest authentic admission"); - assert!(!admit_result_signed(task, task, task, SKILL_GF16_MUL, 0, 0x11, wm, 100, 50, 2000, 10000, 2000, exe, 0xBEEF, 1, 0xBEEF), "front-run rejected"); - assert!(!admit_result_signed(task, task, task, SKILL_GF16_MUL, 0, 0x11, wm, 100, 50, 2000, 10000, 2000, exe, exe, 0, exe), "forge (unsigned) rejected"); - assert!(!admit_result_signed(task, task, task, SKILL_GF16_MUL, 0, 0x11, wm, 100, 50, 2000, 10000, 2000, exe, exe, 1, 0xBEEF), "identity mismatch rejected"); - assert!(!admit_result_signed(task, task, task, SKILL_GF16_MUL, 0, 0x11, wm, 100, 50, 1999, 10000, 2000, exe, exe, 1, exe), "under-collateralized rejected"); + assert!( + admit_result_signed( + task, + task, + task, + SKILL_GF16_MUL, + 0, + 0x11, + wm, + 100, + 50, + 2000, + 10000, + 2000, + exe, + exe, + 1, + exe + ), + "honest authentic admission" + ); + assert!( + !admit_result_signed( + task, + task, + task, + SKILL_GF16_MUL, + 0, + 0x11, + wm, + 100, + 50, + 2000, + 10000, + 2000, + exe, + 0xBEEF, + 1, + 0xBEEF + ), + "front-run rejected" + ); + assert!( + !admit_result_signed( + task, + task, + task, + SKILL_GF16_MUL, + 0, + 0x11, + wm, + 100, + 50, + 2000, + 10000, + 2000, + exe, + exe, + 0, + exe + ), + "forge (unsigned) rejected" + ); + assert!( + !admit_result_signed( + task, + task, + task, + SKILL_GF16_MUL, + 0, + 0x11, + wm, + 100, + 50, + 2000, + 10000, + 2000, + exe, + exe, + 1, + 0xBEEF + ), + "identity mismatch rejected" + ); + assert!( + !admit_result_signed( + task, + task, + task, + SKILL_GF16_MUL, + 0, + 0x11, + wm, + 100, + 50, + 1999, + 10000, + 2000, + exe, + exe, + 1, + exe + ), + "under-collateralized rejected" + ); } #[test] @@ -144,19 +338,119 @@ fn width_binding_closes_precision_downgrade() { // family + op are a lossy proxy: GFT16 and GFT8 share both. assert_eq!(skill_width(SKILL_GFT16_MUL), 16); assert_eq!(skill_width(SKILL_GFT8_MUL), 8); - assert_eq!(skill_op(SKILL_GFT16_MUL), skill_op(SKILL_GFT8_MUL), "GFT16 and GFT8 share op"); - assert_eq!(skill_family(SKILL_GFT16_MUL), skill_family(SKILL_GFT8_MUL), "GFT16 and GFT8 share family"); + assert_eq!( + skill_op(SKILL_GFT16_MUL), + skill_op(SKILL_GFT8_MUL), + "GFT16 and GFT8 share op" + ); + assert_eq!( + skill_family(SKILL_GFT16_MUL), + skill_family(SKILL_GFT8_MUL), + "GFT16 and GFT8 share family" + ); // Binding layer: the bare gate binds a GFT8 receipt to a GFT16 assignment; sized rejects. - assert!(result_binds_assign(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11), "bare gate binds on family+op"); - assert!(!result_binds_assign_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 8), "sized rejects a GFT8-width receipt for a GFT16 assignment"); - assert!(result_binds_assign_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 16), "matching width binds"); + assert!( + result_binds_assign(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11), + "bare gate binds on family+op" + ); + assert!( + !result_binds_assign_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 8), + "sized rejects a GFT8-width receipt for a GFT16 assignment" + ); + assert!( + result_binds_assign_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 16), + "matching width binds" + ); // Full ingress: admit_result_signed is width-blind; admit_result_signed_sized rejects the downgrade. let e = 0xE1u32; - assert!(admit_result_signed(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 0x100, 100, 50, 2000, 10000, 2000, e, e, 1, e), "width-blind full gate admits the downgrade"); - assert!(!admit_result_signed_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 0x100, 100, 50, 2000, 10000, 2000, e, e, 1, e, 8), "sized full gate rejects the GFT8 downgrade"); - assert!(admit_result_signed_sized(0x777, 0x777, 0x777, SKILL_GFT16_MUL, FMT_GFT, 0x11, 0x100, 100, 50, 2000, 10000, 2000, e, e, 1, e, 16), "sized full gate admits the matching width"); + assert!( + admit_result_signed( + 0x777, + 0x777, + 0x777, + SKILL_GFT16_MUL, + FMT_GFT, + 0x11, + 0x100, + 100, + 50, + 2000, + 10000, + 2000, + e, + e, + 1, + e + ), + "width-blind full gate admits the downgrade" + ); + assert!( + !admit_result_signed_sized( + 0x777, + 0x777, + 0x777, + SKILL_GFT16_MUL, + FMT_GFT, + 0x11, + 0x100, + 100, + 50, + 2000, + 10000, + 2000, + e, + e, + 1, + e, + 8 + ), + "sized full gate rejects the GFT8 downgrade" + ); + assert!( + admit_result_signed_sized( + 0x777, + 0x777, + 0x777, + SKILL_GFT16_MUL, + FMT_GFT, + 0x11, + 0x100, + 100, + 50, + 2000, + 10000, + 2000, + e, + e, + 1, + e, + 16 + ), + "sized full gate admits the matching width" + ); // Never rescues a prior-gate failure even at the right width. - assert!(!admit_result_signed_sized(0x777, 0x100, 0x100, SKILL_GFT16_MUL, FMT_GFT, 0x11, 0x100, 100, 50, 2000, 10000, 2000, e, e, 1, e, 16), "right width but stale still rejects"); + assert!( + !admit_result_signed_sized( + 0x777, + 0x100, + 0x100, + SKILL_GFT16_MUL, + FMT_GFT, + 0x11, + 0x100, + 100, + 50, + 2000, + 10000, + 2000, + e, + e, + 1, + e, + 16 + ), + "right width but stale still rejects" + ); } // ---- non-terminal outcomes: challenger accountability ---- @@ -166,13 +460,37 @@ fn challenger_stake_griefing_vs_nonfault() { use challenge::*; // A proven fraud (SLASH), a replay (STALE), and an unprovable split (INDETERMINATE) // all KEEP the challenger's stake -- the dispute was not the challenger's fault. - assert_eq!(challenger_stake_after_bound(50, RESOLVE_SLASH), 50, "correct challenge keeps stake"); - assert_eq!(challenger_stake_after_bound(50, RESOLVE_STALE), 50, "replay is not griefing -> keep"); - assert_eq!(challenger_stake_after_bound(50, RESOLVE_INDETERMINATE), 50, "split proves nothing -> keep"); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_SLASH), + 50, + "correct challenge keeps stake" + ); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_STALE), + 50, + "replay is not griefing -> keep" + ); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_INDETERMINATE), + 50, + "split proves nothing -> keep" + ); // A frivolous (HONEST) or griefing (MALFORMED / FAMILY_MISMATCH) dispute BURNS it. - assert_eq!(challenger_stake_after_bound(50, RESOLVE_HONEST), 0, "frivolous challenge burns stake"); - assert_eq!(challenger_stake_after_bound(50, RESOLVE_MALFORMED), 0, "malformed griefing burns stake"); - assert_eq!(challenger_stake_after_bound(50, RESOLVE_FAMILY_MISMATCH), 0, "family-confusion griefing burns stake"); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_HONEST), + 0, + "frivolous challenge burns stake" + ); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_MALFORMED), + 0, + "malformed griefing burns stake" + ); + assert_eq!( + challenger_stake_after_bound(50, RESOLVE_FAMILY_MISMATCH), + 0, + "family-confusion griefing burns stake" + ); } #[test] @@ -181,7 +499,11 @@ fn verifier_accountability() { // A dissenter from a formed quorum is provably wrong -> stake burned; an agreeing // verifier keeps it. Honest verifiers split the burned dissenter stake. assert_eq!(verifier_stake_after(50, 1), 0, "dissenter burned"); - assert_eq!(verifier_stake_after(50, 0), 50, "agreeing verifier keeps stake"); + assert_eq!( + verifier_stake_after(50, 0), + 50, + "agreeing verifier keeps stake" + ); // 3 honest, 2 dissenters at 50 each -> 100 burned, split among 3 -> 33. assert_eq!(burned_total5(0x41, 0x41, 0x41, 0x99, 0xBE, 50), 100); assert_eq!(honest_share5(0x41, 0x41, 0x41, 0x99, 0xBE, 50), 33); @@ -194,8 +516,14 @@ fn gft_validity_and_finiteness() { use gfvalid::*; // GF-T Et=4: 3^4 = 81 codes; offset_max = 80 is the reserved special row. assert!(is_valid_gft(0, 4), "offset 0 valid"); - assert!(is_valid_gft(79, 4), "offset just below the special row valid"); - assert!(!is_valid_gft(80, 4), "the special row (offset_max) is not a valid value"); + assert!( + is_valid_gft(79, 4), + "offset just below the special row valid" + ); + assert!( + !is_valid_gft(80, 4), + "the special row (offset_max) is not a valid value" + ); assert!(!is_valid_gft(81, 4), "out-of-range offset invalid"); // is_finite_gft16 is the Et=4 alias. assert!(is_finite_gft16(79), "below special row -> finite"); @@ -210,10 +538,22 @@ fn payable_needs_all_four_guards() { // sig + fresh + finite + not-settled -> pays. assert_eq!(payable_authentic(1, 1, 1, 0), 1, "all four -> open"); // dropping ANY single guard closes it. - assert_eq!(payable_authentic(0, 1, 1, 0), 0, "no signature -> closed (forgery)"); + assert_eq!( + payable_authentic(0, 1, 1, 0), + 0, + "no signature -> closed (forgery)" + ); assert_eq!(payable_authentic(1, 0, 1, 0), 0, "stale -> closed (replay)"); - assert_eq!(payable_authentic(1, 1, 0, 0), 0, "inf/nan -> closed (garbage)"); - assert_eq!(payable_authentic(1, 1, 1, 1), 0, "already settled -> closed (double-pay)"); + assert_eq!( + payable_authentic(1, 1, 0, 0), + 0, + "inf/nan -> closed (garbage)" + ); + assert_eq!( + payable_authentic(1, 1, 1, 1), + 0, + "already settled -> closed (double-pay)" + ); } // ---- bitnet: 0b11 decodes to INACTIVE (the packing-malleability fix) ---- @@ -224,7 +564,11 @@ fn bitnet_is_active_treats_0b11_as_inactive() { assert_eq!(is_active(1), 1, "0b01 -> active (+1)"); assert_eq!(is_active(2), 1, "0b10 -> active (-1)"); assert_eq!(is_active(0), 0, "0b00 -> inactive (skip)"); - assert_eq!(is_active(3), 0, "0b11 -> inactive, not a spurious active weight"); + assert_eq!( + is_active(3), + 0, + "0b11 -> inactive, not a spurious active weight" + ); } // ---- GF-T payability is RANGE-checked (out-of-range offset was garbage-paid) ---- @@ -234,14 +578,42 @@ fn gft_payability_rejects_out_of_range() { use settle::*; // in-range finite offsets pay; the special row and anything past it withhold. assert_eq!(payable_flag(FMT_GFT, 0, 0, 0, 0, 80), 1, "offset 0 payable"); - assert_eq!(payable_flag(FMT_GFT, 79, 0, 0, 0, 80), 1, "boundary 79 payable"); - assert_eq!(payable_flag(FMT_GFT, 80, 0, 0, 0, 80), 0, "special row withheld"); - assert_eq!(payable_flag(FMT_GFT, 81, 0, 0, 0, 80), 0, "just past the ladder withheld"); - assert_eq!(payable_flag(FMT_GFT, 100, 0, 0, 0, 80), 0, "out-of-range withheld (was the garbage-pay hole)"); - assert_eq!(payable_flag(FMT_GFT, u32::MAX, 0, 0, 0, 80), 0, "max-u32 garbage withheld"); + assert_eq!( + payable_flag(FMT_GFT, 79, 0, 0, 0, 80), + 1, + "boundary 79 payable" + ); + assert_eq!( + payable_flag(FMT_GFT, 80, 0, 0, 0, 80), + 0, + "special row withheld" + ); + assert_eq!( + payable_flag(FMT_GFT, 81, 0, 0, 0, 80), + 0, + "just past the ladder withheld" + ); + assert_eq!( + payable_flag(FMT_GFT, 100, 0, 0, 0, 80), + 0, + "out-of-range withheld (was the garbage-pay hole)" + ); + assert_eq!( + payable_flag(FMT_GFT, u32::MAX, 0, 0, 0, 80), + 0, + "max-u32 garbage withheld" + ); // and settle actually withholds the reward for the garbage offset. - assert_eq!(settle_checked_gft(100, 16, 1, 100, 80), 100, "out-of-range GF-T settles nothing"); - assert_eq!(settle_checked_gft(100, 16, 1, 40, 80), 116, "in-range GF-T still pays"); + assert_eq!( + settle_checked_gft(100, 16, 1, 100, 80), + 100, + "out-of-range GF-T settles nothing" + ); + assert_eq!( + settle_checked_gft(100, 16, 1, 40, 80), + 116, + "in-range GF-T still pays" + ); } // GF-T payability must AGREE with gfvalid validity: pay iff the offset is a valid @@ -253,7 +625,10 @@ fn gft_payability_matches_validity() { for off in [0u32, 1, 40, 79, 80, 81, 100, 1000, u32::MAX] { let payable = settle::payable_flag(settle::FMT_GFT, off, 0, 0, 0, 80) == 1; let valid = gfvalid::is_valid_gft(off, 4); - assert_eq!(payable, valid, "payable_flag must equal is_valid_gft for offset {off}"); + assert_eq!( + payable, valid, + "payable_flag must equal is_valid_gft for offset {off}" + ); } } @@ -263,16 +638,48 @@ fn gft_payability_matches_validity() { fn binary_payability_withholds_only_inf_nan() { use settle::*; // GF16 (exp_bits 6, mant_bits 9, has_inf 1): all-ones exponent (0x3F) is Inf/NaN. - assert_eq!(payable_flag(FMT_GF_BINARY, 0x4200, 6, 9, 1, 0), 1, "GF16 finite value payable"); - assert_eq!(payable_flag(FMT_GF_BINARY, 0x0000, 6, 9, 1, 0), 1, "GF16 zero (exp 0) is finite -> payable"); - assert_eq!(payable_flag(FMT_GF_BINARY, 0x0001, 6, 9, 1, 0), 1, "GF16 subnormal (exp 0, mant!=0) payable"); - assert_eq!(payable_flag(FMT_GF_BINARY, 0x7E00, 6, 9, 1, 0), 0, "GF16 +Inf (exp all-ones, mant 0) withheld"); - assert_eq!(payable_flag(FMT_GF_BINARY, 0x7E01, 6, 9, 1, 0), 0, "GF16 NaN (exp all-ones, mant!=0) withheld"); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x4200, 6, 9, 1, 0), + 1, + "GF16 finite value payable" + ); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x0000, 6, 9, 1, 0), + 1, + "GF16 zero (exp 0) is finite -> payable" + ); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x0001, 6, 9, 1, 0), + 1, + "GF16 subnormal (exp 0, mant!=0) payable" + ); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x7E00, 6, 9, 1, 0), + 0, + "GF16 +Inf (exp all-ones, mant 0) withheld" + ); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x7E01, 6, 9, 1, 0), + 0, + "GF16 NaN (exp all-ones, mant!=0) withheld" + ); // A format WITHOUT Inf/NaN (has_inf 0, e.g. GF8): every exponent is a normal value. - assert_eq!(payable_flag(FMT_GF_BINARY, 0x70, 3, 4, 0, 0), 1, "GF8 max-exp with no Inf format -> normal, payable"); - assert_eq!(payable_flag(FMT_GF_BINARY, 0x70, 3, 4, 1, 0), 0, "same pattern WITH has_inf -> withheld"); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x70, 3, 4, 0, 0), + 1, + "GF8 max-exp with no Inf format -> normal, payable" + ); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x70, 3, 4, 1, 0), + 0, + "same pattern WITH has_inf -> withheld" + ); // High garbage bits above the 15-bit GF16 field do not change the exp-field // verdict (the mask bounds exp to bits 9..14; the dispute layer catches a wrong // full value). Bit 20 set must not flip the finiteness classification. - assert_eq!(payable_flag(FMT_GF_BINARY, 0x4200, 6, 9, 1, 0), payable_flag(FMT_GF_BINARY, 0x4200 | (1 << 20), 6, 9, 1, 0), "a high garbage bit does not change the exp-field verdict"); + assert_eq!( + payable_flag(FMT_GF_BINARY, 0x4200, 6, 9, 1, 0), + payable_flag(FMT_GF_BINARY, 0x4200 | (1 << 20), 6, 9, 1, 0), + "a high garbage bit does not change the exp-field verdict" + ); }