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 }