lib/machine/src/world/manifest/decode.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const fabric = @import("../../fabric/root.zig");
2 const manifest_types = @import("types.zig");
3 const os = @import("os");
4 const schema = @import("schema.zig");
5 const std = @import("std");
6 const world = @import("../root.zig");
7
8 const wire = os.abi.wire;
9
10 pub fn manifest(
11 input: *const [schema.stream_bytes]u8,
12 expected_root: world.Root,
13 ) manifest_types.Error!world.Cut {
14 try validateEnvelope(input);
15 const node_count = input[schema.RootLayout.node_count_offset];
16 if (node_count == 0 or node_count > world.node_limit) {
17 return error.MetadataShapeMismatch;
18 }
19 try validateReserved(input, node_count);
20 const root_value = try decodeRoot(input);
21 if (!std.meta.eql(root_value, expected_root)) return error.WorldRootMismatch;
22 var result: world.Cut = .{
23 .root = root_value,
24 .nodes = @splat(emptyNode()),
25 };
26 for (&result.nodes, 0..) |*node, index| {
27 if (index == node_count) break;
28 node.* = decodeNode(input, index);
29 }
30 try world.verify(result);
31 std.debug.assert(std.meta.eql(result.root, expected_root));
32 return result;
33 }
34
35 fn validateEnvelope(
36 input: *const [schema.stream_bytes]u8,
37 ) manifest_types.Error!void {
38 const layout = schema.Envelope;
39 if (!std.mem.eql(
40 u8,
41 input[layout.magic_offset..layout.version_offset],
42 &schema.magic,
43 )) return error.BadMagic;
44 if (get16(input, layout.version_offset) != schema.version) {
45 return error.UnsupportedVersion;
46 }
47 if (get16(input, layout.stream_bytes_offset) != schema.stream_bytes) {
48 return error.StreamBytesMismatch;
49 }
50 if (get16(input, layout.flags_offset) != schema.flags) {
51 return error.UnsupportedFlags;
52 }
53 if (get16(input, layout.format_offset) != schema.format) {
54 return error.ManifestFormatMismatch;
55 }
56 if (get16(input, layout.world_dialect_offset) !=
57 @backingInt(world.Dialect.connected_world_v1) or
58 get16(input, layout.fabric_dialect_offset) !=
59 @backingInt(fabric.Dialect.ordered_effect_fabric_v3))
60 {
61 return error.UnknownManifestField;
62 }
63 try validateEnvelopeShape(input);
64 }
65
66 fn validateEnvelopeShape(
67 input: *const [schema.stream_bytes]u8,
68 ) manifest_types.Error!void {
69 const layout = schema.Envelope;
70 if (get16(input, layout.node_capacity_offset) != world.node_limit or
71 get16(input, layout.node_bytes_offset) != schema.node_bytes or
72 get16(input, layout.digest_bytes_offset) != schema.digest_bytes or
73 get16(input, layout.root_bytes_offset) != schema.root_bytes)
74 {
75 return error.MetadataShapeMismatch;
76 }
77 }
78
79 fn validateReserved(
80 input: *const [schema.stream_bytes]u8,
81 node_count: u8,
82 ) manifest_types.Error!void {
83 if (!zeroRange(input, schema.Envelope.reserved_offset, schema.Envelope.limit) or
84 !zeroRange(
85 input,
86 schema.RootLayout.first_reserved_offset,
87 schema.RootLayout.contract_offset,
88 ) or
89 !zeroRange(
90 input,
91 schema.RootLayout.second_reserved_offset,
92 schema.RootLayout.fabric_contract_offset,
93 ) or
94 !zeroRange(
95 input,
96 schema.RootLayout.third_reserved_offset,
97 schema.RootLayout.limit,
98 ) or
99 !wire.allZero(input[schema.material_limit..]))
100 {
101 return error.ReservedNonzero;
102 }
103 try validateNodeReserved(input, node_count);
104 }
105
106 fn validateNodeReserved(
107 input: *const [schema.stream_bytes]u8,
108 node_count: u8,
109 ) manifest_types.Error!void {
110 for (0..world.node_limit) |index| {
111 const start = schema.nodes_offset + index * schema.node_bytes;
112 if (index >= node_count) {
113 if (!wire.allZero(input[start..][0..schema.node_bytes])) {
114 return error.ReservedNonzero;
115 }
116 } else if (!zeroRange(
117 input,
118 start + schema.NodeLayout.reserved_offset,
119 start + schema.NodeLayout.limit,
120 )) {
121 return error.ReservedNonzero;
122 }
123 }
124 }
125
126 fn decodeRoot(
127 input: *const [schema.stream_bytes]u8,
128 ) manifest_types.Error!world.Root {
129 const layout = schema.RootLayout;
130 return .{
131 .digest = digest(input, layout.digest_offset),
132 .dialect = try enumValue(world.Dialect, get16(input, layout.dialect_offset)),
133 .machine_contract = .{ .digest = digest(input, layout.contract_offset) },
134 .fabric = .{
135 .digest = digest(input, layout.fabric_digest_offset),
136 .dialect = try enumValue(
137 fabric.Dialect,
138 get16(input, layout.fabric_dialect_offset),
139 ),
140 .machine_contract = .{
141 .digest = digest(input, layout.fabric_contract_offset),
142 },
143 .entry_frontier = get64(input, layout.entry_frontier_offset),
144 .admission_frontier = get64(input, layout.admission_frontier_offset),
145 .fault_frontier = get64(input, layout.fault_frontier_offset),
146 },
147 .node_count = input[layout.node_count_offset],
148 };
149 }
150
151 fn decodeNode(
152 input: *const [schema.stream_bytes]u8,
153 index: usize,
154 ) world.Node {
155 std.debug.assert(index < world.node_limit);
156 const layout = schema.NodeLayout;
157 const start = schema.nodes_offset + index * schema.node_bytes;
158 return .{
159 .id = .{ .bytes = input[start + layout.id_offset ..][0..16].* },
160 .machine = .{
161 .digest = digest(input, start + layout.root_digest_offset),
162 .profile = .{
163 .digest = digest(input, start + layout.profile_digest_offset),
164 },
165 .state = .{
166 .digest = digest(input, start + layout.state_digest_offset),
167 },
168 },
169 };
170 }
171
172 fn emptyNode() world.Node {
173 return .{
174 .id = .{ .bytes = @splat(0) },
175 .machine = .{
176 .digest = @splat(0),
177 .profile = .{ .digest = @splat(0) },
178 .state = .{ .digest = @splat(0) },
179 },
180 };
181 }
182
183 fn enumValue(comptime T: type, value: anytype) manifest_types.Error!T {
184 inline for (std.meta.tags(T)) |candidate| {
185 if (@backingInt(candidate) == value) return candidate;
186 }
187 return error.UnknownManifestField;
188 }
189
190 fn digest(input: *const [schema.stream_bytes]u8, offset: usize) os.abi.Digest {
191 std.debug.assert(offset + schema.digest_bytes <= input.len);
192 return input[offset..][0..schema.digest_bytes].*;
193 }
194
195 fn zeroRange(
196 input: *const [schema.stream_bytes]u8,
197 first: usize,
198 last: usize,
199 ) bool {
200 std.debug.assert(first <= last);
201 std.debug.assert(last <= input.len);
202 return wire.allZero(input[first..last]);
203 }
204
205 fn get16(input: *const [schema.stream_bytes]u8, offset: usize) u16 {
206 std.debug.assert(offset + 2 <= input.len);
207 return wire.read16(input[offset..][0..2]);
208 }
209
210 fn get64(input: *const [schema.stream_bytes]u8, offset: usize) u64 {
211 std.debug.assert(offset + 8 <= input.len);
212 return wire.read64(input[offset..][0..8]);
213 }