lib/machine/src/fabric/transition/encode.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const admission = @import("../../admission/root.zig");
2 const fabric = @import("../root.zig");
3 const fault = @import("../../fault/root.zig");
4 const os = @import("os");
5 const schema = @import("schema.zig");
6 const std = @import("std");
7 const transition_types = @import("types.zig");
8
9 const wire = os.abi.wire;
10
11 pub fn transition(
12 before: *const fabric.Fabric,
13 value: *const fabric.Transition,
14 output: *[schema.stream_bytes]u8,
15 ) transition_types.Error!void {
16 var candidate = before.*;
17 const result = try candidate.replay(value.entry);
18 if (!std.meta.eql(result, value.root)) return error.TransitionMismatch;
19 try validateEntryTails(value.entry);
20 var encoded: [schema.stream_bytes]u8 = @splat(0);
21 encodeEnvelope(&encoded);
22 encodeRoot(value.entry.previous, schema.Header.previous_offset, &encoded);
23 encodeRoot(value.root, schema.Header.result_offset, &encoded);
24 put64(&encoded, schema.Header.sequence_offset, value.entry.sequence);
25 switch (value.entry.value) {
26 .admission => |entry| {
27 encoded[schema.Header.kind_offset] = 1;
28 encodeAdmission(entry, &encoded);
29 },
30 .settlement => |entry| {
31 encoded[schema.Header.kind_offset] = 2;
32 encodeSettlement(entry, &encoded);
33 },
34 .fault => |entry| {
35 encoded[schema.Header.kind_offset] = 3;
36 encodeFaultEntry(entry, &encoded);
37 },
38 }
39 output.* = encoded;
40 }
41
42 fn encodeEnvelope(output: *[schema.stream_bytes]u8) void {
43 const layout = schema.Envelope;
44 @memcpy(output[layout.magic_offset..layout.version_offset], &schema.magic);
45 put16(output, layout.version_offset, schema.version);
46 put16(output, layout.stream_bytes_offset, schema.stream_bytes);
47 put16(output, layout.flags_offset, schema.flags);
48 put16(output, layout.format_offset, schema.format);
49 put16(
50 output,
51 layout.fabric_dialect_offset,
52 @backingInt(fabric.Dialect.ordered_effect_fabric_v3),
53 );
54 put16(
55 output,
56 layout.fault_dialect_offset,
57 @backingInt(fault.Dialect.deterministic_fault_v1),
58 );
59 put16(output, layout.digest_bytes_offset, schema.digest_bytes);
60 put16(output, layout.root_bytes_offset, schema.root_bytes);
61 put16(output, layout.record_bytes_offset, schema.record_bytes);
62 put16(output, layout.effect_bytes_offset, schema.effect_bytes);
63 put16(output, layout.fault_effect_bytes_offset, schema.fault_effect_bytes);
64 put16(output, layout.decision_bytes_offset, schema.decision_bytes);
65 put16(output, layout.semantic_bytes_offset, schema.semantic_bytes);
66 }
67
68 fn encodeRoot(
69 value: fabric.Root,
70 start: usize,
71 output: *[schema.stream_bytes]u8,
72 ) void {
73 const layout = schema.RootLayout;
74 putDigest(output, start + layout.digest_offset, value.digest);
75 putDigest(output, start + layout.contract_offset, value.machine_contract.digest);
76 put64(output, start + layout.entry_frontier_offset, value.entry_frontier);
77 put64(output, start + layout.admission_frontier_offset, value.admission_frontier);
78 put64(output, start + layout.fault_frontier_offset, value.fault_frontier);
79 put16(output, start + layout.dialect_offset, @backingInt(value.dialect));
80 }
81
82 fn encodeAdmission(
83 value: fabric.AdmissionEntry,
84 output: *[schema.stream_bytes]u8,
85 ) void {
86 const start = schema.Header.limit;
87 const layout = schema.Admission;
88 @memcpy(output[start + layout.node_offset ..][0..16], &value.node.bytes);
89 encodeRecord(value.admission.record, start + layout.record_offset, output);
90 encodeAdmissionEffect(value.effect, start + layout.effect_offset, output);
91 if (value.fault) |fault_value| {
92 output[start + layout.fault_present_offset] = 1;
93 encodeFaultEffect(
94 fault_value.effect,
95 start + layout.fault_effect_offset,
96 output,
97 );
98 encodeDecision(
99 fault_value.decision,
100 start + layout.decision_offset,
101 output,
102 );
103 }
104 output[start + layout.record_kind_offset] = recordKind(value.admission.record);
105 output[start + layout.effect_kind_offset] = effectKind(value.effect);
106 }
107
108 fn encodeRecord(
109 value: admission.Record,
110 start: usize,
111 output: *[schema.stream_bytes]u8,
112 ) void {
113 const layout = schema.Record;
114 switch (value) {
115 .terminal => |record| {
116 put64(output, start + layout.first_offset, record.offset);
117 put16(output, start + layout.payload_length_offset, record.length);
118 @memcpy(
119 output[start + layout.payload_storage_offset ..][0..record.length],
120 record.storage[0..record.length],
121 );
122 },
123 .virtual_time => |record| {
124 put64(output, start + layout.first_offset, record.from_tick);
125 put64(output, start + layout.second_offset, record.to_tick);
126 },
127 .entropy => |record| {
128 put64(output, start + layout.first_offset, record.generation);
129 put16(output, start + layout.payload_length_offset, record.length);
130 @memcpy(
131 output[start + layout.payload_storage_offset ..][0..record.length],
132 record.storage[0..record.length],
133 );
134 },
135 .effect_result => |record| {
136 putDigest(
137 output,
138 start + layout.first_offset,
139 record.request.receipt.digest,
140 );
141 put64(
142 output,
143 start + layout.correlation_offset,
144 record.request.correlation,
145 );
146 put16(output, start + layout.status_offset, @backingInt(record.status));
147 put16(output, start + layout.effect_length_offset, record.length);
148 putDigest(output, start + layout.output_root_offset, record.output_root);
149 @memcpy(
150 output[start + layout.effect_storage_offset ..][0..record.length],
151 record.storage[0..record.length],
152 );
153 },
154 }
155 }
156
157 fn encodeAdmissionEffect(
158 value: fabric.AdmissionEffect,
159 start: usize,
160 output: *[schema.stream_bytes]u8,
161 ) void {
162 const layout = schema.Effect;
163 switch (value) {
164 .direct => {},
165 .packet_send => |packet| {
166 put64(output, start + layout.id_offset, packet.id);
167 @memcpy(output[start + layout.source_offset ..][0..16], &packet.source.bytes);
168 @memcpy(
169 output[start + layout.destination_offset ..][0..16],
170 &packet.destination.bytes,
171 );
172 put16(output, start + layout.channel_offset, packet.channel);
173 put16(output, start + layout.length_offset, packet.length);
174 putDigest(
175 output,
176 start + layout.request_receipt_offset,
177 packet.request.receipt.digest,
178 );
179 put64(
180 output,
181 start + layout.request_correlation_offset,
182 packet.request.correlation,
183 );
184 putDigest(output, start + layout.digest_offset, packet.digest);
185 @memcpy(
186 output[start + layout.storage_offset ..][0..packet.length],
187 packet.storage[0..packet.length],
188 );
189 },
190 .packet_delivery => |packet_id| {
191 put64(output, start + layout.id_offset, packet_id);
192 },
193 }
194 }
195
196 fn encodeSettlement(
197 value: fabric.SettlementEntry,
198 output: *[schema.stream_bytes]u8,
199 ) void {
200 const start = schema.Header.limit;
201 const layout = schema.Settlement;
202 @memcpy(output[start + layout.node_offset ..][0..16], &value.node.bytes);
203 encodeSemantic(value.receipt, start + layout.semantic_offset, output);
204 }
205
206 fn encodeSemantic(
207 value: @FieldType(fabric.SettlementEntry, "receipt"),
208 start: usize,
209 output: *[schema.stream_bytes]u8,
210 ) void {
211 const layout = schema.Semantic;
212 putDigest(output, start + layout.admission_offset, value.admission_receipt.digest);
213 putDigest(output, start + layout.contract_offset, value.basis.contract.digest);
214 putDigest(output, start + layout.source_root_offset, value.basis.source_root);
215 put64(output, start + layout.input_offset, value.basis.frontiers.input);
216 put64(
217 output,
218 start + layout.terminal_input_offset,
219 value.basis.frontiers.terminal_input_offset,
220 );
221 put64(
222 output,
223 start + layout.virtual_time_offset,
224 value.basis.frontiers.virtual_time_tick,
225 );
226 put64(output, start + layout.entropy_offset, value.basis.frontiers.entropy_generation);
227 put64(output, start + layout.effect_offset, value.basis.frontiers.effect);
228 putDigest(output, start + layout.image_offset, value.image_digest);
229 putDigest(output, start + layout.execution_offset, value.execution_fingerprint.digest);
230 put64(output, start + layout.block_generation_offset, value.block_root.generation);
231 putDigest(output, start + layout.block_digest_offset, value.block_root.digest);
232 put64(output, start + layout.request_sequence_offset, value.boundary.request_sequence);
233 put64(output, start + layout.semantic_frontier_offset, value.boundary.semantic_frontier);
234 put64(output, start + layout.effect_frontier_offset, value.boundary.effect_frontier);
235 put64(output, start + layout.terminal_offset, value.boundary.terminal_offset);
236 put64(
237 output,
238 start + layout.boundary_virtual_time_offset,
239 value.boundary.virtual_time_tick,
240 );
241 put64(
242 output,
243 start + layout.boundary_entropy_offset,
244 value.boundary.entropy_generation,
245 );
246 put64(output, start + layout.request_consumed_offset, value.boundary.request_consumed);
247 put64(output, start + layout.request_produced_offset, value.boundary.request_produced);
248 put64(output, start + layout.event_consumed_offset, value.boundary.event_consumed);
249 put64(output, start + layout.event_produced_offset, value.boundary.event_produced);
250 put32(output, start + layout.unresolved_effects_offset, value.boundary.unresolved_effects);
251 put16(output, start + layout.scheduler_offset, @backingInt(value.boundary.scheduler));
252 putDigest(output, start + layout.boundary_block_offset, value.boundary.block_root);
253 put64(output, start + layout.boundary_input_offset, value.boundary.input_frontier);
254 put64(
255 output,
256 start + layout.boundary_terminal_input_offset,
257 value.boundary.terminal_input_offset,
258 );
259 put64(output, start + layout.settled_request_offset, value.settled.request_cursor);
260 put64(output, start + layout.settled_event_offset, value.settled.event_cursor);
261 output[start + layout.k0_offset] = value.k0.counter;
262 putDigest(output, start + layout.transcript_offset, value.semantic_transcript_digest);
263 }
264
265 fn encodeFaultEntry(
266 value: fabric.FaultEntry,
267 output: *[schema.stream_bytes]u8,
268 ) void {
269 const start = schema.Header.limit;
270 encodeFaultEffect(value.effect, start + schema.Fault.effect_offset, output);
271 encodeDecision(value.decision, start + schema.Fault.decision_offset, output);
272 }
273
274 fn encodeFaultEffect(
275 value: fabric.FaultEffect,
276 start: usize,
277 output: *[schema.stream_bytes]u8,
278 ) void {
279 const layout = schema.FaultEffect;
280 output[start + layout.kind_offset] = @backingInt(std.meta.activeTag(value));
281 switch (value) {
282 .machine_crash => |node| putNode(output, start + layout.value_offset, node),
283 .process_crash => |crash| {
284 putNode(output, start + layout.value_offset, crash.node);
285 put64(output, start + layout.second_offset, crash.process);
286 },
287 .io_error => |alternatives| encodeAlternatives(alternatives, start, output),
288 .packet_loss => |packet| put64(output, start + layout.value_offset, packet),
289 .packet_delay => |delay| {
290 put64(output, start + layout.value_offset, delay.packet);
291 put64(output, start + layout.value_offset + 8, delay.until_tick);
292 },
293 .packet_reorder => |reorder| {
294 put64(output, start + layout.value_offset, reorder.first);
295 put64(output, start + layout.value_offset + 8, reorder.second);
296 },
297 .partition => |partition| {
298 putNode(output, start + layout.value_offset, partition.first);
299 putNode(output, start + layout.second_offset, partition.second);
300 output[start + layout.second_offset + 16] = @intFromBool(partition.active);
301 },
302 .clock_jump => |tick| put64(output, start + layout.value_offset, tick),
303 .entropy_choice => |alternatives| encodeAlternatives(alternatives, start, output),
304 .capacity_exhaustion => |capacity| {
305 output[start + layout.value_offset] = @backingInt(capacity.resource);
306 output[start + layout.value_offset + 1] = @intFromBool(capacity.active);
307 },
308 .host_service_failure => |alternatives| encodeAlternatives(alternatives, start, output),
309 }
310 }
311
312 fn encodeAlternatives(
313 value: fabric.FaultAlternatives,
314 start: usize,
315 output: *[schema.stream_bytes]u8,
316 ) void {
317 const layout = schema.FaultEffect;
318 putNode(output, start + layout.value_offset, value.node);
319 putDigest(output, start + layout.second_offset, value.bypass);
320 putDigest(output, start + layout.third_offset, value.inject);
321 }
322
323 fn encodeDecision(
324 value: fault.Decision,
325 start: usize,
326 output: *[schema.stream_bytes]u8,
327 ) void {
328 const layout = schema.Decision;
329 putDigest(output, start + layout.id_offset, value.point.id);
330 put16(output, start + layout.dialect_offset, @backingInt(value.point.dialect));
331 output[start + layout.kind_offset] = @backingInt(value.point.kind);
332 output[start + layout.choice_offset] = @backingInt(value.choice);
333 putDigest(output, start + layout.basis_offset, value.point.basis);
334 putDigest(output, start + layout.subject_offset, value.point.subject);
335 putDigest(output, start + layout.digest_offset, value.digest);
336 }
337
338 fn recordKind(value: admission.Record) u8 {
339 return switch (value) {
340 .terminal => 1,
341 .virtual_time => 2,
342 .entropy => 3,
343 .effect_result => 4,
344 };
345 }
346
347 fn effectKind(value: fabric.AdmissionEffect) u8 {
348 return switch (value) {
349 .direct => 1,
350 .packet_send => 2,
351 .packet_delivery => 3,
352 };
353 }
354
355 fn validateEntryTails(value: fabric.Entry) transition_types.Error!void {
356 if (value.value != .admission) return;
357 const effect = value.value.admission.effect;
358 if (effect != .packet_send) return;
359 const packet = effect.packet_send;
360 if (!wire.allZero(packet.storage[packet.length..])) {
361 return error.TransitionMismatch;
362 }
363 }
364
365 fn putNode(
366 output: *[schema.stream_bytes]u8,
367 offset: usize,
368 value: fabric.NodeId,
369 ) void {
370 std.debug.assert(offset + value.bytes.len <= output.len);
371 @memcpy(output[offset..][0..value.bytes.len], &value.bytes);
372 }
373
374 fn putDigest(
375 output: *[schema.stream_bytes]u8,
376 offset: usize,
377 value: os.abi.Digest,
378 ) void {
379 std.debug.assert(offset + schema.digest_bytes <= output.len);
380 @memcpy(output[offset..][0..schema.digest_bytes], &value);
381 }
382
383 fn put16(output: *[schema.stream_bytes]u8, offset: usize, value: anytype) void {
384 std.debug.assert(offset + 2 <= output.len);
385 wire.write16(output[offset..][0..2], @intCast(value));
386 }
387
388 fn put32(output: *[schema.stream_bytes]u8, offset: usize, value: u32) void {
389 std.debug.assert(offset + 4 <= output.len);
390 wire.write32(output[offset..][0..4], value);
391 }
392
393 fn put64(output: *[schema.stream_bytes]u8, offset: usize, value: u64) void {
394 std.debug.assert(offset + 8 <= output.len);
395 wire.write64(output[offset..][0..8], value);
396 }