lib/machine/src/checkpoint/stream/decode.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const admission = @import("../../admission/root.zig");
2 const core = @import("machine_instance_core");
3 const instance_receipt = @import("../../instance/receipt/root.zig");
4 const os = @import("os");
5 const owner = @import("../owner/root.zig");
6 const schema = @import("schema.zig");
7 const stream_types = @import("types.zig");
8 const std = @import("std");
9
10 const wire = os.abi.wire;
11
12 /// Decodes one fixed 4096-byte header under `expected_root`. The function
13 /// validates the framing, the reserved ranges, the root fields, and the settled
14 /// receipt. A decoded root other than `expected_root` returns
15 /// `CheckpointRootMismatch`. The call returns the material and a memory digest,
16 /// which the caller authenticates against the memory bytes that follow.
17 pub fn header(
18 input: *const [schema.header_bytes]u8,
19 expected_root: owner.Root,
20 ) stream_types.Error!stream_types.Decoded {
21 try validateEnvelope(input);
22 try validateReserved(input);
23 const root_value = try decodeRoot(input);
24 if (!std.meta.eql(root_value, expected_root)) {
25 return error.CheckpointRootMismatch;
26 }
27 const memory: owner.MemoryDigest = .{
28 .digest = digest(input, schema.MemoryLayout.digest_offset),
29 };
30 try validateDigest(memory.digest);
31 const receipt = try decodeSemantic(input);
32 const cpu = decodeCpu(input);
33 return .{
34 .material = .{
35 .profile = root_value.profile,
36 .receipt = receipt,
37 .cpu = cpu,
38 .immutable_image = decodeImmutable(input, cpu),
39 },
40 .memory = memory,
41 };
42 }
43
44 fn validateEnvelope(
45 input: *const [schema.header_bytes]u8,
46 ) stream_types.Error!void {
47 const layout = schema.Envelope;
48 if (!std.mem.eql(
49 u8,
50 input[layout.magic_offset..layout.version_offset],
51 &schema.magic,
52 )) return error.BadMagic;
53 if (get16(input, layout.version_offset) != schema.version) {
54 return error.UnsupportedVersion;
55 }
56 if (get16(input, layout.header_bytes_offset) != schema.header_bytes) {
57 return error.HeaderBytesMismatch;
58 }
59 if (get16(input, layout.flags_offset) != schema.flags) {
60 return error.UnsupportedFlags;
61 }
62 if (get16(input, layout.format_offset) != schema.checkpoint_format) {
63 return error.CheckpointFormatMismatch;
64 }
65 if (get64(input, layout.stream_bytes_offset) != schema.encoded_stream_bytes or
66 get64(input, layout.ram_bytes_offset) != schema.ram_bytes)
67 {
68 return error.StreamBytesMismatch;
69 }
70 if (get32(input, layout.page_bytes_offset) != schema.page_bytes) {
71 return error.PageBytesMismatch;
72 }
73 if (get16(input, layout.abi_major_offset) != schema.abi_major or
74 get16(input, layout.abi_minor_offset) != schema.abi_minor)
75 {
76 return error.AbiVersionMismatch;
77 }
78 try validateShape(input);
79 }
80
81 fn validateShape(
82 input: *const [schema.header_bytes]u8,
83 ) stream_types.Error!void {
84 const envelope = schema.Envelope;
85 const immutable = schema.ImmutableLayout;
86 if (get16(input, envelope.load_capacity_offset) != schema.load_capacity or
87 get16(input, envelope.load_bytes_offset) != schema.load_bytes or
88 get16(input, envelope.digest_bytes_offset) != schema.digest_bytes or
89 get16(input, envelope.root_bytes_offset) != schema.root_bytes or
90 get16(input, envelope.semantic_receipt_bytes_offset) !=
91 schema.semantic_receipt_bytes or
92 get16(input, envelope.cpu_bytes_offset) != schema.cpu_bytes or
93 get16(input, envelope.immutable_descriptor_bytes_offset) !=
94 schema.immutable_descriptor_bytes or
95 get16(input, immutable.load_bytes_offset) != schema.load_bytes)
96 {
97 return error.MetadataShapeMismatch;
98 }
99 }
100
101 fn validateReserved(
102 input: *const [schema.header_bytes]u8,
103 ) stream_types.Error!void {
104 if (!wire.allZero(
105 input[schema.Envelope.reserved_offset..schema.Envelope.limit],
106 ) or !wire.allZero(
107 input[schema.SemanticLayout.reserved_offset..schema.SemanticLayout.transcript_offset],
108 ) or !wire.allZero(
109 input[schema.CpuLayout.reserved_offset..schema.CpuLayout.limit],
110 ) or !wire.allZero(input[schema.ImmutableLayout.limit..])) {
111 return error.ReservedNonzero;
112 }
113 for (0..schema.load_capacity) |index| {
114 const start = schema.ImmutableLayout.loads_offset +
115 index * schema.load_bytes;
116 if (!wire.allZero(input[start + schema.LoadLayout.reserved_offset .. start + schema.LoadLayout.limit])) {
117 return error.ReservedNonzero;
118 }
119 }
120 }
121
122 fn decodeRoot(
123 input: *const [schema.header_bytes]u8,
124 ) stream_types.Error!owner.Root {
125 const value: owner.Root = .{
126 .digest = digest(input, schema.RootLayout.digest_offset),
127 .profile = .{
128 .digest = digest(input, schema.RootLayout.profile_offset),
129 },
130 .state = .{
131 .digest = digest(input, schema.RootLayout.state_offset),
132 },
133 };
134 try validateDigest(value.digest);
135 try validateDigest(value.profile.digest);
136 try validateDigest(value.state.digest);
137 return value;
138 }
139
140 fn decodeSemantic(
141 input: *const [schema.header_bytes]u8,
142 ) stream_types.Error!instance_receipt.SemanticReceipt {
143 const layout = schema.SemanticLayout;
144 const frontiers = decodeFrontiers(input);
145 const semantic_frontier = get64(input, layout.semantic_frontier_offset);
146 const block_generation = std.math.add(
147 u64,
148 semantic_frontier,
149 1,
150 ) catch return error.CheckpointCorrupt;
151 const block_digest = digest(input, layout.block_root_offset);
152 const settled_request = get64(input, layout.settled_request_offset);
153 const event_produced = get64(input, layout.event_produced_offset);
154 const value: instance_receipt.SemanticReceipt = .{
155 .admission_receipt = .{ .digest = digest(input, layout.admission_offset) },
156 .basis = .{
157 .contract = .{ .digest = digest(input, layout.contract_offset) },
158 .source_root = digest(input, layout.source_root_offset),
159 .frontiers = frontiers,
160 .outstanding_effect = null,
161 },
162 .image_digest = digest(input, layout.image_offset),
163 .execution_fingerprint = .{
164 .digest = digest(input, layout.execution_offset),
165 },
166 .block_root = .{
167 .generation = block_generation,
168 .digest = block_digest,
169 },
170 .boundary = decodeBoundary(
171 input,
172 frontiers,
173 semantic_frontier,
174 block_digest,
175 settled_request,
176 event_produced,
177 ),
178 .settled = .{
179 .request_cursor = settled_request,
180 .event_cursor = event_produced,
181 },
182 .k0 = .{ .counter = input[layout.k0_offset] },
183 .semantic_transcript_digest = digest(input, layout.transcript_offset),
184 };
185 try instance_receipt.verifySemantic(value);
186 return value;
187 }
188
189 fn decodeFrontiers(
190 input: *const [schema.header_bytes]u8,
191 ) admission.Frontiers {
192 const layout = schema.SemanticLayout;
193 return .{
194 .input = get64(input, layout.input_frontier_offset),
195 .terminal_input_offset = get64(input, layout.terminal_input_offset),
196 .virtual_time_tick = get64(input, layout.virtual_time_offset),
197 .entropy_generation = get64(input, layout.entropy_offset),
198 .effect = get64(input, layout.effect_offset),
199 };
200 }
201
202 fn decodeBoundary(
203 input: *const [schema.header_bytes]u8,
204 frontiers: admission.Frontiers,
205 semantic_frontier: u64,
206 block_digest: os.abi.Digest,
207 settled_request: u64,
208 event_produced: u64,
209 ) instance_receipt.SemanticBoundary {
210 const layout = schema.SemanticLayout;
211 return .{
212 .request_sequence = settled_request,
213 .semantic_frontier = semantic_frontier,
214 .effect_frontier = frontiers.effect,
215 .terminal_offset = get64(input, layout.terminal_offset),
216 .virtual_time_tick = frontiers.virtual_time_tick,
217 .entropy_generation = frontiers.entropy_generation,
218 .request_consumed = settled_request,
219 .request_produced = settled_request,
220 .event_consumed = get64(input, layout.event_consumed_offset),
221 .event_produced = event_produced,
222 .unresolved_effects = 0,
223 .scheduler = .idle,
224 .block_root = block_digest,
225 .input_frontier = frontiers.input,
226 .terminal_input_offset = frontiers.terminal_input_offset,
227 };
228 }
229
230 fn decodeCpu(input: *const [schema.header_bytes]u8) owner.CpuState {
231 const layout = schema.CpuLayout;
232 return .{
233 .rip = get64(input, layout.rip_offset),
234 .rsp = get64(input, layout.rsp_offset),
235 .rdi = get64(input, layout.rdi_offset),
236 .rflags = get64(input, layout.rflags_offset),
237 .cr0 = get64(input, layout.cr0_offset),
238 .cr3 = get64(input, layout.cr3_offset),
239 .cr4 = get64(input, layout.cr4_offset),
240 .efer = get64(input, layout.efer_offset),
241 .code_selector = get16(input, layout.code_selector_offset),
242 .data_selector = get16(input, layout.data_selector_offset),
243 };
244 }
245
246 fn decodeImmutable(
247 input: *const [schema.header_bytes]u8,
248 cpu: owner.CpuState,
249 ) core.provenance.ImmutableImage {
250 const layout = schema.ImmutableLayout;
251 var loads: [schema.load_capacity]core.provenance.ImmutableLoad = @splat(.{});
252 for (&loads, 0..) |*load, index| {
253 load.* = decodeLoad(input, layout.loads_offset + index * schema.load_bytes);
254 }
255 return .{
256 .digest = digest(input, layout.digest_offset),
257 .initial = cpu,
258 .entry_offset = get32(input, layout.entry_offset),
259 .load_count = get16(input, layout.load_count_offset),
260 .loads = loads,
261 };
262 }
263
264 fn decodeLoad(
265 input: *const [schema.header_bytes]u8,
266 start: usize,
267 ) core.provenance.ImmutableLoad {
268 const layout = schema.LoadLayout;
269 return .{
270 .source_offset = get32(input, start + layout.source_offset),
271 .file_bytes = get32(input, start + layout.file_offset),
272 .memory_bytes = get32(input, start + layout.memory_offset),
273 .physical_offset = get32(input, start + layout.physical_offset),
274 .virtual_offset = get64(input, start + layout.virtual_offset),
275 .flags = get32(input, start + layout.flags_offset),
276 };
277 }
278
279 fn digest(
280 input: *const [schema.header_bytes]u8,
281 offset: usize,
282 ) os.abi.Digest {
283 return input[offset..][0..schema.digest_bytes].*;
284 }
285
286 fn validateDigest(value: os.abi.Digest) owner.Error!void {
287 wire.validateDigest(value) catch return error.CheckpointCorrupt;
288 }
289
290 fn get16(input: *const [schema.header_bytes]u8, offset: usize) u16 {
291 return wire.read16(input[offset..][0..2]);
292 }
293
294 fn get32(input: *const [schema.header_bytes]u8, offset: usize) u32 {
295 return wire.read32(input[offset..][0..4]);
296 }
297
298 fn get64(input: *const [schema.header_bytes]u8, offset: usize) u64 {
299 return wire.read64(input[offset..][0..8]);
300 }