nx_bit_provenance.nx source
↩ module page · 182 lines · 6155 B
1// nx_bit_provenance.nx -- bit-provenance ledger (silicon-to-death tracking).
2//
3// Cardinal: feedback-no-skips-bit-provenance-silicon-to-death.
4//
5// Every output bit the substrate produces carries an ID and an upstream
6// chain. Every consumer (the bit's "death") joins its observation to
7// the chain. Auditable end-to-end: query the ledger for orphans
8// (consumed-without-producer) and zombies (produced-without-consumer)
9// to find substrate honesty bugs.
10//
11// API surface (V0):
12//
13// let id: i64 = nx_bprov_alloc()
14// nx_bprov_emit(id, src_a_id, src_b_id, layer_tag, primitive_tag)
15// nx_bprov_emit3(id, src_a_id, src_b_id, src_c_id, layer_tag, primitive_tag)
16// nx_bprov_join(in_bit_id, layer_tag, primitive_tag)
17// nx_bprov_audit_zombies() -> i64 -- 0 = clean
18// nx_bprov_lookup_join(id) -> i64 -- the consumer layer, -1 if unjoined
19// nx_bprov_n_records() -> i64
20//
21// Layer tags (sealed enum matching SMOKE_SCHEMA.md + CAR_DIAGRAM.md):
22
23import "syscalls.nx"
24const NX_MAGIC_12345: i64 = 12345
25const NX_MAGIC_67890: i64 = 67890
26const NX_MAGIC_11111: i64 = 11111
27const NX_MAGIC_22222: i64 = 22222
28const NX_MAGIC_33333: i64 = 33333
29const NX_MAGIC_44444: i64 = 44444
30const NX_MAGIC_99999: i64 = 99999
31
32const NX_BPROV_L_NEG_1: i64 = -1
33const NX_BPROV_L_0: i64 = 0
34const NX_BPROV_L_1: i64 = 1
35const NX_BPROV_L_1S: i64 = 2
36const NX_BPROV_L_2: i64 = 3
37const NX_BPROV_L_3: i64 = 4
38const NX_BPROV_L_35: i64 = 5
39const NX_BPROV_L_36: i64 = 6
40const NX_BPROV_L_37: i64 = 7
41const NX_BPROV_L_4: i64 = 8
42const NX_BPROV_L_5: i64 = 9
43const NX_BPROV_L_6: i64 = 10
44const NX_BPROV_L_7: i64 = 11
45const NX_BPROV_L_IMG: i64 = 12
46const NX_BPROV_L_RC: i64 = 13
47
48const NX_BPROV_REC_SIZE: i64 = 64 // 8 i64 fields
49const NX_BPROV_CHUNK_BYTES: i64 = 262144 // 4096 records
50
51static NX_BPROV_LEDGER_PTR: i64
52static NX_BPROV_NEXT_ID: i64
53static NX_BPROV_N_RECORDS: i64
54static NX_BPROV_CAPACITY: i64
55
56func nx_bprov_init() -> i64 {
57 if NX_BPROV_LEDGER_PTR != 0 { return 0 }
58 let raw: *u8 = sys_mmap(NX_BPROV_CHUNK_BYTES)
59 if (raw as i64) == 0 { return 1 }
60 if (raw as i64) == 0 - 1 { return 2 }
61 NX_BPROV_LEDGER_PTR = raw as i64
62 NX_BPROV_NEXT_ID = 1
63 NX_BPROV_N_RECORDS = 0
64 NX_BPROV_CAPACITY = NX_BPROV_CHUNK_BYTES / NX_BPROV_REC_SIZE
65 return 0
66}
67
68func nx_bprov_alloc() -> i64 {
69 nx_bprov_init()
70 let id: i64 = NX_BPROV_NEXT_ID
71 NX_BPROV_NEXT_ID = id + 1
72 return id
73}
74
75func _nx_bprov_write(id: i64, src_a: i64, src_b: i64, src_c: i64,
76 layer: i64, primitive: i64) -> i64 {
77 nx_bprov_init()
78 if NX_BPROV_N_RECORDS >= NX_BPROV_CAPACITY { return 3 }
79 let base: i64 = NX_BPROV_LEDGER_PTR + (NX_BPROV_N_RECORDS * NX_BPROV_REC_SIZE)
80 let rec: *i64 = base as *i64
81 rec[0] = id
82 rec[1] = src_a
83 rec[2] = src_b
84 rec[3] = src_c
85 rec[4] = layer
86 rec[5] = primitive
87 rec[6] = 0 // join_layer (only valid when rec[7] == 1)
88 rec[7] = 0 // joined_flag: 0 = zombie, 1 = joined
89 NX_BPROV_N_RECORDS = NX_BPROV_N_RECORDS + 1
90 return 0
91}
92
93func nx_bprov_emit(id: i64, src_a: i64, src_b: i64,
94 layer: i64, primitive: i64) -> i64 {
95 return _nx_bprov_write(id, src_a, src_b, 0, layer, primitive)
96}
97
98func nx_bprov_emit3(id: i64, src_a: i64, src_b: i64, src_c: i64,
99 layer: i64, primitive: i64) -> i64 {
100 return _nx_bprov_write(id, src_a, src_b, src_c, layer, primitive)
101}
102
103func nx_bprov_join(in_bit_id: i64, layer: i64, primitive: i64) -> i64 {
104 nx_bprov_init()
105 var i: i64 = 0
106 while i < NX_BPROV_N_RECORDS {
107 let base: i64 = NX_BPROV_LEDGER_PTR + (i * NX_BPROV_REC_SIZE)
108 let rec: *i64 = base as *i64
109 if rec[0] == in_bit_id {
110 rec[6] = layer
111 // primitive tag stored in slot 8 via a separate alloc;
112 // V0: we keep just layer + joined_flag, primitive is
113 // logged in trace. Could extend rec to 9 fields later.
114 rec[7] = 1 // joined
115 return 0
116 }
117 i = i + 1
118 }
119 return 1
120}
121
122func nx_bprov_audit_zombies() -> i64 {
123 nx_bprov_init()
124 var n: i64 = 0
125 var i: i64 = 0
126 while i < NX_BPROV_N_RECORDS {
127 let base: i64 = NX_BPROV_LEDGER_PTR + (i * NX_BPROV_REC_SIZE)
128 let rec: *i64 = base as *i64
129 if rec[7] == 0 { n = n + 1 } // joined_flag=0 -> zombie
130 i = i + 1
131 }
132 return n
133}
134
135func nx_bprov_lookup_join(in_bit_id: i64) -> i64 {
136 nx_bprov_init()
137 var i: i64 = 0
138 while i < NX_BPROV_N_RECORDS {
139 let base: i64 = NX_BPROV_LEDGER_PTR + (i * NX_BPROV_REC_SIZE)
140 let rec: *i64 = base as *i64
141 if rec[0] == in_bit_id {
142 if rec[7] == 0 { return 0 - 2 } // unjoined sentinel
143 return rec[6] // joined layer
144 }
145 i = i + 1
146 }
147 return 0 - 1 // record not found
148}
149
150func nx_bprov_n_records() -> i64 {
151 nx_bprov_init()
152 return NX_BPROV_N_RECORDS
153}
154
155// ---- self-test ---------------------------------------------------
156// expect_exit: 0
157
158func main() -> i64 {
159 if nx_bprov_init() != 0 { return 1 }
160 let id_a: i64 = nx_bprov_alloc()
161 if id_a != 1 { return 2 }
162 let id_b: i64 = nx_bprov_alloc()
163 if id_b != 2 { return 3 }
164 if nx_bprov_emit(id_b, id_a, 0, NX_BPROV_L_3, NX_MAGIC_12345) != 0 { return 4 }
165 if nx_bprov_join(id_b, NX_BPROV_L_7, NX_MAGIC_67890) != 0 { return 5 }
166 if nx_bprov_lookup_join(id_b) != NX_BPROV_L_7 { return 6 }
167
168 let id_c: i64 = nx_bprov_alloc()
169 if nx_bprov_emit(id_c, id_a, id_b, NX_BPROV_L_4, NX_MAGIC_11111) != 0 { return 7 }
170 if nx_bprov_audit_zombies() != 1 { return 8 }
171 if nx_bprov_join(id_c, NX_BPROV_L_IMG, NX_MAGIC_22222) != 0 { return 9 }
172 if nx_bprov_audit_zombies() != 0 { return 10 }
173
174 let id_d: i64 = nx_bprov_alloc()
175 if nx_bprov_emit3(id_d, id_a, id_b, id_c, NX_BPROV_L_35, NX_MAGIC_33333) != 0 { return 11 }
176 if nx_bprov_join(id_d, NX_BPROV_L_4, NX_MAGIC_44444) != 0 { return 12 }
177
178 if nx_bprov_lookup_join(NX_MAGIC_99999) != 0 - 1 { return 13 }
179 if nx_bprov_n_records() != 3 { return 14 }
180
181 return 0
182}