nx_etg_byzantine.nx source
↩ module page · 126 lines · 5093 B
1// nx_etg_byzantine.nx -- E2.5 Byzantine M-of-N outcome agreement.
2//
3// Anti-forgery gate for ETG attestations. Composes E1 NxEtgEntry +
4// E2 tournament selector + E3 per-tier probes by running the same
5// probe N times (across diverse-double-compile + canary-tagged
6// probe binaries per [[NISHI_RACING_CREW_ROADMAP]]) and requiring
7// M-of-N replicas to agree on the outcome before the substrate
8// commits the attestation to its journal.
9//
10// Detects:
11// - Compromised probe binary lying about silicon truth
12// - Transient measurement noise that flipped a single replica
13// - Vendor-driven runtime gating that only triggers on specific
14// probe call patterns (probe diversity defeats this)
15// - Wheeler diverse-double-compile detection of compiler-side
16// attestation forgery (different compilers should produce the
17// same outcome on the same silicon -- if they don't, agreement
18// fails loud, never silent)
19//
20// Composes:
21// [[NISHI_ETG_ROADMAP]] E2.5 (this is the gate primitive)
22// nx_etg.nx (E1 NxEtgEntry; SHIPPED)
23// [[NISHI_RACING_CREW_ROADMAP]] (Byzantine N-of-M mechanism)
24// [[feedback-racing-crew-team-honesty-threat-aware]] (the threat
25// model this gate addresses)
26// [[feedback-no-false-ok-substrate-honesty-audit]] (NO_AGREEMENT
27// is the substrate-honest verdict; never silent picks of
28// minority-majority)
29//
30// API:
31// nx_etg_byzantine_outcome_agree(outcomes, n, m_required) -> i64
32// Returns the agreed outcome (NX_ETG_OUTCOME_*) iff some outcome
33// occurs >= m_required times in the input. Else returns
34// NX_ETG_BYZ_NO_AGREEMENT. Tie-break: if multiple outcomes
35// reach the m_required threshold, returns the FIRST one found
36// by walking outcome ids 0..NX_ETG_OUTCOME_N-1 (deterministic
37// replay).
38//
39// nx_etg_byzantine_count_outcome(outcomes, n, target) -> i64
40// Counts occurrences of `target` in the outcomes array. Useful
41// for auditing (how confident was the agreement?).
42//
43// Why outcome-only agreement (not measurement_value agreement):
44// Real measurement values have noise -- syscall round-trips,
45// cache latency, even integer-ALU throughput per nx_calibrate
46// vary cycle-to-cycle. Requiring bit-equal measurement across
47// replicas would mean NO_AGREEMENT on every real probe. The
48// substrate-honest gate is OUTCOME agreement: did all replicas
49// classify into the same NX_ETG_OUTCOME_*? Measurement-aggregation
50// (median / trimmed-mean / interquartile-range) is a separate
51// primitive queued for E2.6 once the agreement layer is proven.
52
53// nx_safety_envelope:
54// intended_use: "Byzantine M-of-N outcome agreement; gates
55// ETG attestation commits; substrate-honest
56// on disagreement (NO_AGREEMENT sentinel)"
57// sil_target: SIL2
58// evidence: [kat_unanimous_agreement,
59// kat_majority_agreement,
60// kat_no_majority_no_agreement,
61// kat_threshold_boundary_correctness,
62// kat_invalid_m_returns_no_agreement,
63// kat_deterministic_tie_break]
64// hazard_register: [bug-tape-off-by-one-on-m-threshold,
65// bug-tape-tie-break-nondeterministic,
66// bug-tape-invalid-m-silently-accepted]
67// verdict: NOT_YET_EVALUATED
68
69import "nx_syscalls.nx"
70import "nx_etg.nx"
71
72// ===== Sentinel ===================================================
73
74const NX_ETG_BYZ_NO_AGREEMENT: i64 = -1
75
76// ===== Outcome counting ===========================================
77
78// Walk the outcomes array; count occurrences of `target`. Used by
79// the agreement function + exposed for auditors.
80func nx_etg_byzantine_count_outcome(
81 outcomes: *i64,
82 n: i64,
83 target: i64
84) -> i64 {
85 if n <= 0 { return 0 }
86 var i: i64 = 0
87 var count: i64 = 0
88 while i < n {
89 if outcomes[i] == target {
90 count = count + 1
91 }
92 i = i + 1
93 }
94 return count
95}
96
97// ===== M-of-N agreement ===========================================
98//
99// Returns the agreed outcome (lowest outcome id reaching the
100// threshold) or NX_ETG_BYZ_NO_AGREEMENT if no outcome does.
101//
102// Walks outcome ids 0..NX_ETG_OUTCOME_N-1 in order; for each id,
103// asks the counting helper how many of the N replicas voted for it;
104// returns the first id whose count >= m_required. Walking in id
105// order ensures deterministic tie-break (lowest id wins if two
106// outcomes reach the threshold).
107
108func nx_etg_byzantine_outcome_agree(
109 outcomes: *i64,
110 n: i64,
111 m_required: i64
112) -> i64 {
113 if n <= 0 { return NX_ETG_BYZ_NO_AGREEMENT }
114 if m_required <= 0 { return NX_ETG_BYZ_NO_AGREEMENT }
115 if m_required > n { return NX_ETG_BYZ_NO_AGREEMENT }
116
117 var oid: i64 = 0
118 while oid < NX_ETG_OUTCOME_N {
119 let c: i64 = nx_etg_byzantine_count_outcome(outcomes, n, oid)
120 if c >= m_required {
121 return oid
122 }
123 oid = oid + 1
124 }
125 return NX_ETG_BYZ_NO_AGREEMENT
126}