lib/machine/src/fabric/test.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const admission = @import("../admission/root.zig");
2 const fabric = @import("root.zig");
3 const fault = @import("../fault/root.zig");
4 const instance = @import("../instance/root.zig");
5 const os = @import("os");
6 const profile = @import("../profile/root.zig");
7 const std = @import("std");
8
9 const k0_elf = @embedFile("machine-k0-elf");
10 const k0_manifest = @embedFile("machine-k0-manifest");
11 const world: [16]u8 = @splat(0x44);
12 const source_id: fabric.NodeId = .{ .bytes = @splat(0x11) };
13 const destination_id: fabric.NodeId = .{ .bytes = @splat(0x22) };
14 const receiver_id: fabric.NodeId = .{ .bytes = @splat(0x33) };
15
16 const SnapshotMutation = struct {
17 offset: usize,
18 mask: u8,
19 expected: anyerror,
20 };
21
22 const connected_transition_count: usize = 5;
23 const transition_count_max: usize = 6;
24
25 var first_ram: [instance.ram_bytes]u8 align(instance.ram_alignment) = undefined;
26 var second_ram: [instance.ram_bytes]u8 align(instance.ram_alignment) = undefined;
27 var third_ram: [instance.ram_bytes]u8 align(instance.ram_alignment) = undefined;
28 var snapshot_stream: [fabric.snapshot.stream_bytes + 1]u8 = undefined;
29 var transition_streams: [transition_count_max][fabric.transition.stream_bytes]u8 = undefined;
30 var transition_roots: [transition_count_max]fabric.Root = undefined;
31
32 test "fabric snapshot reconstructs exact connected state" {
33 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
34 const first_fence = activationFence(1, 0xf1);
35 const second_fence = activationFence(1, 0xf2);
36 const receiver_fence = activationFence(1, 0xf3);
37 var first_storage = instance.Storage.init();
38 var first = try start(
39 &first_storage,
40 &first_ram,
41 machineInput(execution, first_fence, 0xd1, effectRequest(0xa1)),
42 );
43 defer first.deinit();
44 var second_storage = instance.Storage.init();
45 var second = try start(
46 &second_storage,
47 &second_ram,
48 machineInput(execution, second_fence, 0xd2, effectRequest(0xa2)),
49 );
50 defer second.deinit();
51 var receiver_storage = instance.Storage.init();
52 var receiver = try start(
53 &receiver_storage,
54 &third_ram,
55 machineInput(execution, receiver_fence, 0xd3, effectRequest(0xa3)),
56 );
57 defer receiver.deinit();
58 const connected = try snapshotFixture(
59 &first,
60 &second,
61 &receiver,
62 first_fence,
63 second_fence,
64 );
65 try expectSnapshotRoundTrip(connected);
66 }
67
68 test "fabric snapshot rejects framing tampering aliases and pending turns" {
69 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
70 const first_fence = activationFence(1, 0xf4);
71 const second_fence = activationFence(1, 0xf5);
72 var first_storage = instance.Storage.init();
73 var first = try start(
74 &first_storage,
75 &first_ram,
76 machineInput(execution, first_fence, 0xe1, effectRequest(0xb1)),
77 );
78 defer first.deinit();
79 var second_storage = instance.Storage.init();
80 var second = try start(
81 &second_storage,
82 &second_ram,
83 machineInput(execution, second_fence, 0xe2, effectRequest(0xb2)),
84 );
85 defer second.deinit();
86 const initial = try twoNodeFabric(&first, &second);
87 try expectSnapshotRejections(initial, &first, first_fence);
88 }
89
90 test "fabric transitions reopen one authenticated connected history" {
91 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
92 const source_fence = activationFence(1, 0x91);
93 const destination_fence = activationFence(1, 0x92);
94 var source_storage = instance.Storage.init();
95 var source = try start(
96 &source_storage,
97 &first_ram,
98 machineInput(execution, source_fence, 0x61, effectRequest(0xc1)),
99 );
100 defer source.deinit();
101 var destination_storage = instance.Storage.init();
102 var destination = try start(
103 &destination_storage,
104 &second_ram,
105 machineInput(execution, destination_fence, 0x71, effectRequest(0xc2)),
106 );
107 defer destination.deinit();
108 const initial = try twoNodeFabric(&source, &destination);
109 try encodeSnapshot(&initial);
110 var connected = initial;
111 try recordConnectedTransitions(
112 &connected,
113 &source,
114 &destination,
115 source_fence,
116 destination_fence,
117 );
118 try expectTransitionReplay(initial, connected.root());
119 try expectTransitionRejections(initial);
120 try expectFaultTransitionVariants(initial);
121 }
122
123 test "fabric transitions preserve direct input and admission fault variants" {
124 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
125 const initial_fence = activationFence(1, 0x93);
126 var storage = instance.Storage.init();
127 var machine = try start(
128 &storage,
129 &first_ram,
130 machineInput(execution, initial_fence, 0x81, null),
131 );
132 defer machine.deinit();
133 const initial = try singleNodeFabric(&machine);
134 var connected = initial;
135 try recordDirectTransitions(&connected, &machine, initial_fence);
136 try expectTransitionFrames(initial, transition_count_max, connected.root());
137 }
138
139 test "connected packet turns are write-ahead settled and replayable" {
140 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
141 const source_fence = activationFence(1, 0x51);
142 const destination_fence = activationFence(1, 0x61);
143 var source_storage = instance.Storage.init();
144 var source = try start(
145 &source_storage,
146 &first_ram,
147 machineInput(execution, source_fence, 0x31, effectRequest(0x71)),
148 );
149 defer source.deinit();
150 var destination_storage = instance.Storage.init();
151 var destination = try start(
152 &destination_storage,
153 &second_ram,
154 machineInput(execution, destination_fence, 0x41, effectRequest(0x81)),
155 );
156 defer destination.deinit();
157 const initial = try twoNodeFabric(&source, &destination);
158 try expectRootHex(
159 initial.root(),
160 "f90d9b4855abe5e4927604b39405eb0fb48c8bcda853ee80d0720bfdea393fee",
161 );
162 try exercisePacketReplay(
163 initial,
164 &source,
165 &destination,
166 source_fence,
167 destination_fence,
168 );
169 }
170
171 test "virtual time entropy and terminal choices share the fabric ledger" {
172 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
173 var current_fence = activationFence(1, 0xa1);
174 var storage = instance.Storage.init();
175 var machine = try start(
176 &storage,
177 &first_ram,
178 machineInput(execution, current_fence, 0x51, null),
179 );
180 defer machine.deinit();
181 const nodes = [_]fabric.Node{
182 .{ .id = source_id, .basis = try machine.admissionBasis() },
183 };
184 const initial = try fabric.Fabric.init(.{
185 .profile = profile.interpretedContinuationTestV1(),
186 .world = world,
187 .virtual_time_tick = 7,
188 .nodes = &nodes,
189 });
190
191 var replay = initial;
192 var connected = try advanceAndSettleTime(
193 initial,
194 &machine,
195 current_fence,
196 &replay,
197 );
198 current_fence = activationFence(2, 0xa2);
199 try machine.reactivate(current_fence);
200 try activate(&machine);
201 try std.testing.expectError(
202 error.VirtualTimeRegression,
203 connected.advanceTime(source_id, &machine, current_fence, 7),
204 );
205 connected = try injectAndSettleEntropy(
206 connected,
207 &machine,
208 current_fence,
209 &replay,
210 );
211 current_fence = activationFence(3, 0xa3);
212 try machine.reactivate(current_fence);
213 try activate(&machine);
214 const admission_frontier = connected.root().admission_frontier;
215 const terminal = try connected.terminal(
216 source_id,
217 &machine,
218 current_fence,
219 os.k0.request_bytes,
220 );
221 try std.testing.expectEqual(
222 @as(u64, admission_frontier + 1),
223 terminal.root.admission_frontier,
224 );
225 }
226
227 test "packet selection is an explicit connected schedule choice" {
228 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
229 const first_fence = activationFence(1, 0xc1);
230 const second_fence = activationFence(1, 0xc2);
231 const receiver_fence = activationFence(1, 0xc3);
232 var first_storage = instance.Storage.init();
233 var first = try start(
234 &first_storage,
235 &first_ram,
236 machineInput(execution, first_fence, 0x71, effectRequest(0xd1)),
237 );
238 defer first.deinit();
239 var second_storage = instance.Storage.init();
240 var second = try start(
241 &second_storage,
242 &second_ram,
243 machineInput(execution, second_fence, 0x81, effectRequest(0xd2)),
244 );
245 defer second.deinit();
246 var receiver_storage = instance.Storage.init();
247 var receiver = try start(
248 &receiver_storage,
249 &third_ram,
250 machineInput(execution, receiver_fence, 0x91, effectRequest(0xd3)),
251 );
252 defer receiver.deinit();
253 try expectPacketSelection(
254 &first,
255 &second,
256 &receiver,
257 first_fence,
258 second_fence,
259 receiver_fence,
260 );
261 }
262
263 test "fabric initialization rejects every noncanonical topology" {
264 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
265 const current_fence = activationFence(1, 0xb1);
266 var storage = instance.Storage.init();
267 var machine = try start(
268 &storage,
269 &first_ram,
270 machineInput(execution, current_fence, 0x61, null),
271 );
272 defer machine.deinit();
273 const basis = try machine.admissionBasis();
274 const reversed = [_]fabric.Node{
275 .{ .id = destination_id, .basis = basis },
276 .{ .id = source_id, .basis = basis },
277 };
278 try expectInitError(error.NodesNotCanonical, world, 7, &reversed);
279 const duplicate = [_]fabric.Node{
280 .{ .id = source_id, .basis = basis },
281 .{ .id = source_id, .basis = basis },
282 };
283 try expectInitError(error.NodesNotCanonical, world, 7, &duplicate);
284 const invalid_id = [_]fabric.Node{
285 .{ .id = .{ .bytes = @splat(0) }, .basis = basis },
286 };
287 try expectInitError(error.InvalidNode, world, 7, &invalid_id);
288 const one = [_]fabric.Node{
289 .{ .id = source_id, .basis = basis },
290 };
291 try expectInitError(error.InvalidWorld, @splat(0), 7, &one);
292 try expectInitError(error.NodeStateMismatch, world, 6, &one);
293 const too_many = [_]fabric.Node{
294 .{ .id = .{ .bytes = @splat(1) }, .basis = basis },
295 .{ .id = .{ .bytes = @splat(2) }, .basis = basis },
296 .{ .id = .{ .bytes = @splat(3) }, .basis = basis },
297 .{ .id = .{ .bytes = @splat(4) }, .basis = basis },
298 .{ .id = .{ .bytes = @splat(5) }, .basis = basis },
299 };
300 try expectInitError(error.NodeCapacityExceeded, world, 7, &too_many);
301 const exact = too_many[0..fabric.node_limit];
302 const initialized = try fabric.Fabric.init(.{
303 .profile = profile.interpretedContinuationTestV1(),
304 .world = world,
305 .virtual_time_tick = 7,
306 .nodes = exact,
307 });
308 try std.testing.expectEqual(@as(u64, 0), initialized.root().entry_frontier);
309 try std.testing.expectEqual(
310 fabric.Dialect.ordered_effect_fabric_v3,
311 initialized.root().dialect,
312 );
313 const accelerated = try fabric.Fabric.init(.{
314 .profile = profile.kvmContinuationTestV1(),
315 .world = world,
316 .virtual_time_tick = 7,
317 .nodes = exact,
318 });
319 try expectBackendNeutralFault(initialized, accelerated);
320 var wrong_limit = initialized;
321 wrong_limit.state.admission_limit += 1;
322 try std.testing.expectError(
323 error.FabricStateInvalid,
324 wrong_limit.packet(1),
325 );
326 }
327
328 fn expectBackendNeutralFault(
329 interpreted: fabric.Fabric,
330 accelerated: fabric.Fabric,
331 ) !void {
332 try std.testing.expectEqualDeep(interpreted.root(), accelerated.root());
333 var interpreted_fault = interpreted;
334 var accelerated_fault = accelerated;
335 const fault_input: fabric.FaultInput = .{
336 .effect = .{ .clock_jump = 8 },
337 .choice = .inject,
338 };
339 const interpreted_result = try interpreted_fault.applyFault(fault_input);
340 const accelerated_result = try accelerated_fault.applyFault(fault_input);
341 try std.testing.expectEqualDeep(
342 interpreted_result.decision,
343 accelerated_result.decision,
344 );
345 try std.testing.expectEqualDeep(
346 interpreted_result.root,
347 accelerated_result.root,
348 );
349 }
350
351 test "direct fault choices are stable replayable and enforced" {
352 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
353 const current_fence = activationFence(1, 0xe1);
354 var storage = instance.Storage.init();
355 var machine = try start(
356 &storage,
357 &first_ram,
358 machineInput(execution, current_fence, 0xa1, null),
359 );
360 defer machine.deinit();
361 const initial = try singleNodeFabric(&machine);
362 try expectNodeFaultChoices(initial, &machine, current_fence);
363 try expectClockAndCapacityFaults(initial, &machine, current_fence);
364 }
365
366 test "network faults control delivery without ambient network state" {
367 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
368 const first_fence = activationFence(1, 0xe2);
369 const second_fence = activationFence(1, 0xe3);
370 const receiver_fence = activationFence(1, 0xe4);
371 var first_storage = instance.Storage.init();
372 var first = try start(
373 &first_storage,
374 &first_ram,
375 machineInput(execution, first_fence, 0xb1, effectRequest(0xe1)),
376 );
377 defer first.deinit();
378 var second_storage = instance.Storage.init();
379 var second = try start(
380 &second_storage,
381 &second_ram,
382 machineInput(execution, second_fence, 0xb2, effectRequest(0xe2)),
383 );
384 defer second.deinit();
385 var receiver_storage = instance.Storage.init();
386 var receiver = try start(
387 &receiver_storage,
388 &third_ram,
389 machineInput(execution, receiver_fence, 0xb3, effectRequest(0xe3)),
390 );
391 defer receiver.deinit();
392 const connected = try readyPackets(
393 &first,
394 &second,
395 &receiver,
396 first_fence,
397 second_fence,
398 );
399 try expectNetworkFaultChoices(connected, &receiver, receiver_fence);
400 }
401
402 test "fault alternatives bind entropy and service admissions" {
403 const execution = try os.boot.kernel.manifest.parse(k0_manifest);
404 const entropy_fence = activationFence(1, 0xe5);
405 const service_fence = activationFence(1, 0xe6);
406 var entropy_storage = instance.Storage.init();
407 var entropy_machine = try start(
408 &entropy_storage,
409 &first_ram,
410 machineInput(execution, entropy_fence, 0xc1, null),
411 );
412 defer entropy_machine.deinit();
413 var service_storage = instance.Storage.init();
414 var service_machine = try start(
415 &service_storage,
416 &second_ram,
417 machineInput(execution, service_fence, 0xc2, effectRequest(0xe4)),
418 );
419 defer service_machine.deinit();
420 const initial = try twoNodeFabric(&entropy_machine, &service_machine);
421 const connected = try expectEntropyFaultChoices(
422 initial,
423 &entropy_machine,
424 entropy_fence,
425 );
426 try expectServiceFaultChoices(connected, &service_machine, service_fence);
427 }
428
429 fn singleNodeFabric(machine: *const instance.Instance) !fabric.Fabric {
430 const nodes = [_]fabric.Node{
431 .{ .id = source_id, .basis = try machine.admissionBasis() },
432 };
433 return fabric.Fabric.init(.{
434 .profile = profile.interpretedContinuationTestV1(),
435 .world = world,
436 .virtual_time_tick = 7,
437 .nodes = &nodes,
438 });
439 }
440
441 fn expectNodeFaultChoices(
442 initial: fabric.Fabric,
443 machine: *const instance.Instance,
444 fence: os.abi.ActivationFence,
445 ) !void {
446 const effect: fabric.FaultEffect = .{ .machine_crash = source_id };
447 var bypass_branch = initial;
448 const bypassed = try bypass_branch.applyFault(.{
449 .effect = effect,
450 .choice = .bypass,
451 });
452 var inject_branch = initial;
453 const injected = try inject_branch.applyFault(.{
454 .effect = effect,
455 .choice = .inject,
456 });
457 try std.testing.expectEqualDeep(
458 bypassed.decision.point,
459 injected.decision.point,
460 );
461 try expectDifferentRoots(bypassed.root, injected.root);
462 try std.testing.expect(try bypass_branch.nodeAvailable(source_id));
463 try std.testing.expect(!try inject_branch.nodeAvailable(source_id));
464 try std.testing.expectEqual(@as(u64, 1), injected.root.fault_frontier);
465 try std.testing.expectEqual(@as(u64, 1), injected.root.admission_frontier);
466 try std.testing.expectError(
467 error.NodeUnavailable,
468 inject_branch.terminal(source_id, machine, fence, "blocked"),
469 );
470 try expectFaultReplayMutation(initial, injected);
471 try expectProcessFaultCommand(initial);
472 }
473
474 fn expectFaultReplayMutation(
475 initial: fabric.Fabric,
476 injected: fabric.Faulted,
477 ) !void {
478 var replay = initial;
479 try std.testing.expectEqualDeep(injected.root, try replay.replay(injected.entry));
480 var changed = injected.entry;
481 changed.value.fault.decision.choice = .bypass;
482 var invalid = initial;
483 const before = invalid.root();
484 try std.testing.expectError(
485 error.InvalidFaultDecision,
486 invalid.replay(changed),
487 );
488 try std.testing.expectEqualDeep(before, invalid.root());
489 var changed_effect = injected.entry;
490 changed_effect.value.fault.effect = .{ .clock_jump = 12 };
491 var invalid_effect = initial;
492 const effect_before = invalid_effect.root();
493 try std.testing.expectError(
494 error.InvalidFaultPoint,
495 invalid_effect.replay(changed_effect),
496 );
497 try std.testing.expectEqualDeep(effect_before, invalid_effect.root());
498 }
499
500 fn expectProcessFaultCommand(initial: fabric.Fabric) !void {
501 var process_branch = initial;
502 const command = try process_branch.applyFault(.{
503 .effect = .{ .process_crash = .{ .node = source_id, .process = 7 } },
504 .choice = .inject,
505 });
506 try std.testing.expectEqual(fault.Kind.process_crash, command.decision.point.kind);
507 try std.testing.expect(try process_branch.nodeAvailable(source_id));
508 var invalid = initial;
509 try std.testing.expectError(
510 error.ProcessIdentityInvalid,
511 invalid.applyFault(.{
512 .effect = .{ .process_crash = .{ .node = source_id, .process = 0 } },
513 .choice = .inject,
514 }),
515 );
516 }
517
518 fn expectClockAndCapacityFaults(
519 initial: fabric.Fabric,
520 machine: *const instance.Instance,
521 fence: os.abi.ActivationFence,
522 ) !void {
523 const jump_effect: fabric.FaultEffect = .{ .clock_jump = 12 };
524 var bypass_branch = initial;
525 const bypassed = try bypass_branch.applyFault(.{
526 .effect = jump_effect,
527 .choice = .bypass,
528 });
529 var jump_branch = initial;
530 const jumped = try jump_branch.applyFault(.{
531 .effect = jump_effect,
532 .choice = .inject,
533 });
534 try std.testing.expectEqualDeep(bypassed.decision.point, jumped.decision.point);
535 try std.testing.expectEqual(@as(u64, 12), jump_branch.state.virtual_time_tick);
536 try std.testing.expectError(
537 error.VirtualTimeRegression,
538 jump_branch.advanceTime(source_id, machine, fence, 8),
539 );
540 var capacity = initial;
541 _ = try capacity.applyFault(.{
542 .effect = .{ .capacity_exhaustion = .{
543 .resource = .admission,
544 .active = true,
545 } },
546 .choice = .inject,
547 });
548 try std.testing.expectError(
549 error.AdmissionCapacityExceeded,
550 capacity.terminal(source_id, machine, fence, "blocked"),
551 );
552 _ = try capacity.applyFault(.{
553 .effect = .{ .capacity_exhaustion = .{
554 .resource = .admission,
555 .active = false,
556 } },
557 .choice = .inject,
558 });
559 _ = try capacity.terminal(source_id, machine, fence, "restored");
560 }
561
562 fn expectNetworkFaultChoices(
563 connected: fabric.Fabric,
564 receiver: *const instance.Instance,
565 fence: os.abi.ActivationFence,
566 ) !void {
567 try expectPacketReorderFault(connected, receiver, fence);
568 try expectPacketLossAndDelay(connected, receiver, fence);
569 try expectPartitionFault(connected, receiver, fence);
570 }
571
572 fn expectPacketReorderFault(
573 connected: fabric.Fabric,
574 receiver: *const instance.Instance,
575 fence: os.abi.ActivationFence,
576 ) !void {
577 var reversed = connected;
578 try std.testing.expectError(
579 error.PacketOrderInvalid,
580 reversed.applyFault(.{
581 .effect = .{ .packet_reorder = .{ .first = 2, .second = 1 } },
582 .choice = .inject,
583 }),
584 );
585 const effect: fabric.FaultEffect = .{ .packet_reorder = .{
586 .first = 1,
587 .second = 2,
588 } };
589 var bypass_branch = connected;
590 const bypassed = try bypass_branch.applyFault(.{
591 .effect = effect,
592 .choice = .bypass,
593 });
594 var inject_branch = connected;
595 const injected = try inject_branch.applyFault(.{
596 .effect = effect,
597 .choice = .inject,
598 });
599 try std.testing.expectEqualDeep(
600 bypassed.decision.point,
601 injected.decision.point,
602 );
603 try expectDifferentRoots(bypassed.root, injected.root);
604 const first = try bypass_branch.receiveNextPacket(receiver_id, receiver, fence);
605 const second = try inject_branch.receiveNextPacket(receiver_id, receiver, fence);
606 try std.testing.expectEqual(
607 @as(u64, 1),
608 first.entry.value.admission.effect.packet_delivery,
609 );
610 try std.testing.expectEqual(
611 @as(u64, 2),
612 second.entry.value.admission.effect.packet_delivery,
613 );
614 }
615
616 fn expectPacketLossAndDelay(
617 connected: fabric.Fabric,
618 receiver: *const instance.Instance,
619 fence: os.abi.ActivationFence,
620 ) !void {
621 var lost = connected;
622 const dropped = try lost.applyFault(.{
623 .effect = .{ .packet_loss = 1 },
624 .choice = .inject,
625 });
626 try std.testing.expectEqual(@as(u8, 1), lost.packetCount());
627 try std.testing.expectError(error.PacketUnknown, lost.packet(1));
628 var replay = connected;
629 try std.testing.expectEqualDeep(dropped.root, try replay.replay(dropped.entry));
630 var delayed = connected;
631 try std.testing.expectError(
632 error.PacketDelayed,
633 delayed.applyFault(.{
634 .effect = .{ .packet_delay = .{ .packet = 1, .until_tick = 7 } },
635 .choice = .inject,
636 }),
637 );
638 _ = try delayed.applyFault(.{
639 .effect = .{ .packet_delay = .{ .packet = 1, .until_tick = 12 } },
640 .choice = .inject,
641 });
642 try std.testing.expectError(
643 error.PacketDelayed,
644 delayed.receivePacket(receiver_id, receiver, fence, 1),
645 );
646 _ = try delayed.applyFault(.{
647 .effect = .{ .clock_jump = 12 },
648 .choice = .inject,
649 });
650 _ = try delayed.receivePacket(receiver_id, receiver, fence, 1);
651 }
652
653 fn expectPartitionFault(
654 connected: fabric.Fabric,
655 receiver: *const instance.Instance,
656 fence: os.abi.ActivationFence,
657 ) !void {
658 var partition = connected;
659 try std.testing.expectError(
660 error.InvalidNode,
661 partition.applyFault(.{
662 .effect = .{ .partition = .{
663 .first = receiver_id,
664 .second = source_id,
665 .active = true,
666 } },
667 .choice = .inject,
668 }),
669 );
670 _ = try partition.applyFault(.{
671 .effect = .{ .partition = .{
672 .first = source_id,
673 .second = receiver_id,
674 .active = true,
675 } },
676 .choice = .inject,
677 });
678 try std.testing.expect(try partition.partitioned(source_id, receiver_id));
679 try std.testing.expectError(
680 error.LinkPartitioned,
681 partition.receivePacket(receiver_id, receiver, fence, 1),
682 );
683 _ = try partition.applyFault(.{
684 .effect = .{ .partition = .{
685 .first = source_id,
686 .second = receiver_id,
687 .active = false,
688 } },
689 .choice = .inject,
690 });
691 try std.testing.expect(!try partition.partitioned(source_id, receiver_id));
692 _ = try partition.receivePacket(receiver_id, receiver, fence, 1);
693 }
694
695 fn expectEntropyFaultChoices(
696 initial: fabric.Fabric,
697 machine: *instance.Instance,
698 fence: os.abi.ActivationFence,
699 ) !fabric.Fabric {
700 var equal_branch = initial;
701 const equal_before = equal_branch.root();
702 try std.testing.expectError(
703 error.FaultAlternativesEqual,
704 equal_branch.chooseEntropy(
705 source_id,
706 machine,
707 fence,
708 &.{ 1, 2, 3 },
709 &.{ 1, 2, 3 },
710 .inject,
711 ),
712 );
713 try std.testing.expectEqualDeep(equal_before, equal_branch.root());
714 var bypass_branch = initial;
715 const bypassed = try bypass_branch.chooseEntropy(
716 source_id,
717 machine,
718 fence,
719 &.{ 1, 2, 3 },
720 &.{ 4, 5, 6 },
721 .bypass,
722 );
723 var inject_branch = initial;
724 const injected = try inject_branch.chooseEntropy(
725 source_id,
726 machine,
727 fence,
728 &.{ 1, 2, 3 },
729 &.{ 4, 5, 6 },
730 .inject,
731 );
732 try std.testing.expectEqualDeep(
733 admissionFaultDecision(bypassed).point,
734 admissionFaultDecision(injected).point,
735 );
736 try expectDifferentRoots(bypassed.root, injected.root);
737 try std.testing.expectEqual(@as(u64, 1), injected.root.fault_frontier);
738 var replay = initial;
739 try std.testing.expectEqualDeep(injected.root, try replay.replay(injected.entry));
740 try expectAdmissionFaultMutation(initial, injected);
741 const settled = try inject_branch.settle(
742 source_id,
743 try completeTurn(machine, injected.delivery),
744 );
745 try std.testing.expectEqualDeep(settled.root, try replay.replay(settled.entry));
746 return inject_branch;
747 }
748
749 fn expectAdmissionFaultMutation(
750 initial: fabric.Fabric,
751 admitted: fabric.Admitted,
752 ) !void {
753 var changed = admitted.entry;
754 changed.value.admission.fault.?.decision.choice = .bypass;
755 var replay = initial;
756 const before = replay.root();
757 try std.testing.expectError(
758 error.InvalidFaultDecision,
759 replay.replay(changed),
760 );
761 try std.testing.expectEqualDeep(before, replay.root());
762 var changed_effect = admitted.entry;
763 changed_effect.value.admission.fault.?.effect.entropy_choice.inject[0] ^= 1;
764 var effect_replay = initial;
765 const effect_before = effect_replay.root();
766 try std.testing.expectError(
767 error.InvalidFaultPoint,
768 effect_replay.replay(changed_effect),
769 );
770 try std.testing.expectEqualDeep(effect_before, effect_replay.root());
771 }
772
773 fn expectServiceFaultChoices(
774 connected: fabric.Fabric,
775 machine: *const instance.Instance,
776 fence: os.abi.ActivationFence,
777 ) !void {
778 const normal: fabric.ServiceResult = .{
779 .status = .ok,
780 .output_root = @splat(0xf1),
781 .bytes = "normal",
782 };
783 const io_failure: fabric.ServiceResult = .{
784 .status = .failed,
785 .output_root = @splat(0xf2),
786 .bytes = "io-error",
787 };
788 var bypass_branch = connected;
789 const bypassed = try bypass_branch.chooseService(
790 destination_id,
791 machine,
792 fence,
793 .io_error,
794 normal,
795 io_failure,
796 .bypass,
797 );
798 var inject_branch = connected;
799 const injected = try inject_branch.chooseService(
800 destination_id,
801 machine,
802 fence,
803 .io_error,
804 normal,
805 io_failure,
806 .inject,
807 );
808 try std.testing.expectEqualDeep(
809 admissionFaultDecision(bypassed).point,
810 admissionFaultDecision(injected).point,
811 );
812 try expectDifferentRoots(bypassed.root, injected.root);
813 try std.testing.expectEqual(
814 os.abi.EffectStatus.failed,
815 injected.entry.value.admission.admission.record.effect_result.status,
816 );
817 try expectHostServiceFault(connected, machine, fence, normal);
818 }
819
820 fn expectHostServiceFault(
821 connected: fabric.Fabric,
822 machine: *const instance.Instance,
823 fence: os.abi.ActivationFence,
824 normal: fabric.ServiceResult,
825 ) !void {
826 const unavailable: fabric.ServiceResult = .{
827 .status = .unavailable,
828 .output_root = @splat(0xf3),
829 .bytes = "offline",
830 };
831 var branch = connected;
832 const admitted = try branch.chooseService(
833 destination_id,
834 machine,
835 fence,
836 .host_service_failure,
837 normal,
838 unavailable,
839 .inject,
840 );
841 try std.testing.expectEqual(
842 fault.Kind.host_service_failure,
843 admissionFaultDecision(admitted).point.kind,
844 );
845 var invalid = connected;
846 try std.testing.expectError(
847 error.FaultStatusInvalid,
848 invalid.chooseService(
849 destination_id,
850 machine,
851 fence,
852 .host_service_failure,
853 normal,
854 .{ .status = .failed, .output_root = @splat(0xf4), .bytes = "bad" },
855 .inject,
856 ),
857 );
858 }
859
860 fn admissionFaultDecision(admitted: fabric.Admitted) fault.Decision {
861 return admitted.entry.value.admission.fault.?.decision;
862 }
863
864 fn advanceAndSettleTime(
865 initial: fabric.Fabric,
866 machine: *instance.Instance,
867 fence: os.abi.ActivationFence,
868 replay: *fabric.Fabric,
869 ) !fabric.Fabric {
870 var time_eight = initial;
871 const advanced = try time_eight.advanceTime(source_id, machine, fence, 8);
872 var time_nine = initial;
873 const later = try time_nine.advanceTime(source_id, machine, fence, 9);
874 try expectDifferentRoots(advanced.root, later.root);
875 try std.testing.expectEqualDeep(advanced.root, try replay.replay(advanced.entry));
876 const settled = try time_eight.settle(
877 source_id,
878 try completeTurn(machine, advanced.delivery),
879 );
880 try std.testing.expectEqualDeep(settled.root, try replay.replay(settled.entry));
881 return time_eight;
882 }
883
884 fn injectAndSettleEntropy(
885 initial: fabric.Fabric,
886 machine: *instance.Instance,
887 fence: os.abi.ActivationFence,
888 replay: *fabric.Fabric,
889 ) !fabric.Fabric {
890 var first_branch = initial;
891 const first = try first_branch.injectEntropy(
892 source_id,
893 machine,
894 fence,
895 &.{ 1, 2, 3 },
896 );
897 var second_branch = initial;
898 const second = try second_branch.injectEntropy(
899 source_id,
900 machine,
901 fence,
902 &.{ 1, 2, 4 },
903 );
904 try expectDifferentRoots(first.root, second.root);
905 try std.testing.expectEqualDeep(first.root, try replay.replay(first.entry));
906 const settled = try first_branch.settle(
907 source_id,
908 try completeTurn(machine, first.delivery),
909 );
910 try std.testing.expectEqualDeep(settled.root, try replay.replay(settled.entry));
911 return first_branch;
912 }
913
914 fn expectPacketSelection(
915 first: *instance.Instance,
916 second: *instance.Instance,
917 receiver: *const instance.Instance,
918 first_fence: os.abi.ActivationFence,
919 second_fence: os.abi.ActivationFence,
920 receiver_fence: os.abi.ActivationFence,
921 ) !void {
922 const connected = try readyPackets(
923 first,
924 second,
925 receiver,
926 first_fence,
927 second_fence,
928 );
929 try std.testing.expectEqual(@as(u8, 2), connected.packetCount());
930 try expectPacketBranches(connected, receiver, receiver_fence);
931 }
932
933 fn readyPackets(
934 first: *instance.Instance,
935 second: *instance.Instance,
936 receiver: *const instance.Instance,
937 first_fence: os.abi.ActivationFence,
938 second_fence: os.abi.ActivationFence,
939 ) !fabric.Fabric {
940 const nodes = [_]fabric.Node{
941 .{ .id = source_id, .basis = try first.admissionBasis() },
942 .{ .id = destination_id, .basis = try second.admissionBasis() },
943 .{ .id = receiver_id, .basis = try receiver.admissionBasis() },
944 };
945 var connected = try fabric.Fabric.init(.{
946 .profile = profile.interpretedContinuationTestV1(),
947 .world = world,
948 .virtual_time_tick = 7,
949 .nodes = &nodes,
950 });
951 const first_send = try connected.sendPacket(
952 source_id,
953 first,
954 first_fence,
955 .{ .destination = receiver_id, .channel = 9, .bytes = "first" },
956 );
957 _ = try connected.settle(
958 source_id,
959 try completeTurn(first, first_send.delivery),
960 );
961 const second_send = try connected.sendPacket(
962 destination_id,
963 second,
964 second_fence,
965 .{ .destination = receiver_id, .channel = 9, .bytes = "second" },
966 );
967 _ = try connected.settle(
968 destination_id,
969 try completeTurn(second, second_send.delivery),
970 );
971 return connected;
972 }
973
974 fn expectPacketBranches(
975 connected: fabric.Fabric,
976 receiver: *const instance.Instance,
977 fence: os.abi.ActivationFence,
978 ) !void {
979 var choose_first = connected;
980 const first = try choose_first.receivePacket(receiver_id, receiver, fence, 1);
981 var choose_second = connected;
982 const second = try choose_second.receivePacket(receiver_id, receiver, fence, 2);
983 try expectDifferentRoots(first.root, second.root);
984 try std.testing.expectEqual(@as(u8, 1), choose_first.packetCount());
985 try std.testing.expectEqual(@as(u8, 1), choose_second.packetCount());
986 try std.testing.expectError(error.PacketUnknown, choose_first.packet(1));
987 try std.testing.expectError(error.PacketUnknown, choose_second.packet(2));
988 try std.testing.expectEqualStrings("second", (try choose_first.packet(2)).bytes());
989 try std.testing.expectEqualStrings("first", (try choose_second.packet(1)).bytes());
990 }
991
992 fn twoNodeFabric(
993 source: *const instance.Instance,
994 destination: *const instance.Instance,
995 ) !fabric.Fabric {
996 const nodes = [_]fabric.Node{
997 .{ .id = source_id, .basis = try source.admissionBasis() },
998 .{ .id = destination_id, .basis = try destination.admissionBasis() },
999 };
1000 return fabric.Fabric.init(.{
1001 .profile = profile.interpretedContinuationTestV1(),
1002 .world = world,
1003 .virtual_time_tick = 7,
1004 .nodes = &nodes,
1005 });
1006 }
1007
1008 fn exercisePacketReplay(
1009 initial: fabric.Fabric,
1010 source: *instance.Instance,
1011 destination: *instance.Instance,
1012 source_fence: os.abi.ActivationFence,
1013 destination_fence: os.abi.ActivationFence,
1014 ) !void {
1015 try expectPacketPayloadCapacity(initial, source, source_fence);
1016 try expectPacketCapacityFault(initial, source, source_fence);
1017 try expectServiceBranching(initial, source, source_fence);
1018 var connected = initial;
1019 const sent = try connected.sendPacket(
1020 source_id,
1021 source,
1022 source_fence,
1023 .{ .destination = destination_id, .channel = 7, .bytes = "packet" },
1024 );
1025 try expectPendingSafety(
1026 initial,
1027 &connected,
1028 destination,
1029 source_fence,
1030 destination_fence,
1031 sent,
1032 );
1033 var replay = initial;
1034 try std.testing.expectEqualDeep(sent.root, try replay.replay(sent.entry));
1035 const source_settled = try connected.settle(
1036 source_id,
1037 try completeTurn(source, sent.delivery),
1038 );
1039 try std.testing.expectEqualDeep(
1040 source_settled.root,
1041 try replay.replay(source_settled.entry),
1042 );
1043 try std.testing.expect(!connected.turnPending());
1044 try expectReceiptDivergence(initial, sent, source_settled);
1045 const received = try connected.receivePacket(
1046 destination_id,
1047 destination,
1048 destination_fence,
1049 1,
1050 );
1051 try std.testing.expectEqual(@as(u8, 0), connected.packetCount());
1052 try std.testing.expectEqualDeep(received.root, try replay.replay(received.entry));
1053 const destination_settled = try connected.settle(
1054 destination_id,
1055 try completeTurn(destination, received.delivery),
1056 );
1057 try std.testing.expectEqualDeep(
1058 destination_settled.root,
1059 try replay.replay(destination_settled.entry),
1060 );
1061 try std.testing.expectEqualDeep(connected.root(), replay.root());
1062 try expectRootHex(
1063 connected.root(),
1064 "09f26db64cd8b9a5abbf87e264a00e12c8eb35e14172ed9d64e11a3544e22952",
1065 );
1066 }
1067
1068 fn recordConnectedTransitions(
1069 connected: *fabric.Fabric,
1070 source: *instance.Instance,
1071 destination: *instance.Instance,
1072 source_fence: os.abi.ActivationFence,
1073 destination_fence: os.abi.ActivationFence,
1074 ) !void {
1075 const before_send = connected.*;
1076 const sent = try connected.sendPacket(
1077 source_id,
1078 source,
1079 source_fence,
1080 .{ .destination = destination_id, .channel = 9, .bytes = "durable" },
1081 );
1082 try recordTransition(0, &before_send, sent.entry, sent.root);
1083 const source_receipt = try completeTurn(source, sent.delivery);
1084 const before_source_settle = connected.*;
1085 const source_settled = try connected.settle(source_id, source_receipt);
1086 try recordTransition(
1087 1,
1088 &before_source_settle,
1089 source_settled.entry,
1090 source_settled.root,
1091 );
1092 const before_receive = connected.*;
1093 const received = try connected.receivePacket(
1094 destination_id,
1095 destination,
1096 destination_fence,
1097 1,
1098 );
1099 try recordTransition(2, &before_receive, received.entry, received.root);
1100 const destination_receipt = try completeTurn(destination, received.delivery);
1101 const before_destination_settle = connected.*;
1102 const destination_settled = try connected.settle(
1103 destination_id,
1104 destination_receipt,
1105 );
1106 try recordTransition(
1107 3,
1108 &before_destination_settle,
1109 destination_settled.entry,
1110 destination_settled.root,
1111 );
1112 const before_fault = connected.*;
1113 const faulted = try connected.applyFault(.{
1114 .effect = .{ .clock_jump = 19 },
1115 .choice = .inject,
1116 });
1117 try recordTransition(4, &before_fault, faulted.entry, faulted.root);
1118 }
1119
1120 fn recordTransition(
1121 index: usize,
1122 before: *const fabric.Fabric,
1123 entry: fabric.Entry,
1124 root: fabric.Root,
1125 ) !void {
1126 std.debug.assert(index < transition_count_max);
1127 const value: fabric.Transition = .{ .entry = entry, .root = root };
1128 var writer = std.Io.Writer.fixed(&transition_streams[index]);
1129 try fabric.transition.encodeDisjoint(before, &value, &writer);
1130 try std.testing.expectEqual(fabric.transition.stream_bytes, writer.end);
1131 transition_roots[index] = root;
1132 }
1133
1134 fn recordDirectTransitions(
1135 connected: *fabric.Fabric,
1136 machine: *instance.Instance,
1137 initial_fence: os.abi.ActivationFence,
1138 ) !void {
1139 const before_time = connected.*;
1140 const advanced = try connected.advanceTime(
1141 source_id,
1142 machine,
1143 initial_fence,
1144 11,
1145 );
1146 try recordTransition(0, &before_time, advanced.entry, advanced.root);
1147 const time_receipt = try completeTurn(machine, advanced.delivery);
1148 const before_time_settle = connected.*;
1149 const time_settled = try connected.settle(source_id, time_receipt);
1150 try recordTransition(1, &before_time_settle, time_settled.entry, time_settled.root);
1151 const entropy_fence = activationFence(2, 0x94);
1152 try machine.reactivate(entropy_fence);
1153 try activate(machine);
1154 const before_entropy = connected.*;
1155 const entropy = try connected.chooseEntropy(
1156 source_id,
1157 machine,
1158 entropy_fence,
1159 &.{ 1, 2, 3 },
1160 &.{ 4, 5, 6 },
1161 .inject,
1162 );
1163 try recordTransition(2, &before_entropy, entropy.entry, entropy.root);
1164 const entropy_receipt = try completeTurn(machine, entropy.delivery);
1165 const before_entropy_settle = connected.*;
1166 const entropy_settled = try connected.settle(source_id, entropy_receipt);
1167 try recordTransition(
1168 3,
1169 &before_entropy_settle,
1170 entropy_settled.entry,
1171 entropy_settled.root,
1172 );
1173 const terminal_fence = activationFence(3, 0x95);
1174 try machine.reactivate(terminal_fence);
1175 try activate(machine);
1176 const before_terminal = connected.*;
1177 const terminal = try connected.terminal(
1178 source_id,
1179 machine,
1180 terminal_fence,
1181 os.k0.request_bytes,
1182 );
1183 try recordTransition(4, &before_terminal, terminal.entry, terminal.root);
1184 const terminal_receipt = try completeTurn(machine, terminal.delivery);
1185 const before_terminal_settle = connected.*;
1186 const terminal_settled = try connected.settle(source_id, terminal_receipt);
1187 try recordTransition(
1188 5,
1189 &before_terminal_settle,
1190 terminal_settled.entry,
1191 terminal_settled.root,
1192 );
1193 }
1194
1195 fn expectTransitionReplay(
1196 initial: fabric.Fabric,
1197 final_root: fabric.Root,
1198 ) !void {
1199 try std.testing.expectEqualSlices(u8, &.{
1200 'M', 'C', 'H', 'F', 'T', 'R', '1', 0,
1201 1, 0, 0, 4, 0, 0, 1, 0,
1202 3, 0, 1, 0, 32, 0, 96, 0,
1203 208, 0, 240, 0, 88, 0, 136, 0,
1204 192, 1,
1205 }, transition_streams[0][0..34]);
1206 var snapshot_reader = std.Io.Reader.fixed(
1207 snapshot_stream[0..fabric.snapshot.stream_bytes],
1208 );
1209 const replay = try fabric.snapshot.decodeDisjoint(
1210 &snapshot_reader,
1211 initial.root(),
1212 );
1213 try expectTransitionFrames(
1214 replay,
1215 connected_transition_count,
1216 final_root,
1217 );
1218 }
1219
1220 fn expectTransitionFrames(
1221 initial: fabric.Fabric,
1222 count: usize,
1223 final_root: fabric.Root,
1224 ) !void {
1225 std.debug.assert(count > 0);
1226 std.debug.assert(count <= transition_count_max);
1227 var replay = initial;
1228 for (0..count) |index| {
1229 const before = replay;
1230 var reader = std.Io.Reader.fixed(&transition_streams[index]);
1231 const value = try fabric.transition.replayDisjoint(
1232 &replay,
1233 &reader,
1234 transition_roots[index],
1235 );
1236 try std.testing.expectEqualDeep(transition_roots[index], value.root);
1237 var repeated: [fabric.transition.stream_bytes]u8 = undefined;
1238 var writer = std.Io.Writer.fixed(&repeated);
1239 try fabric.transition.encodeDisjoint(&before, &value, &writer);
1240 try std.testing.expectEqualSlices(
1241 u8,
1242 &transition_streams[index],
1243 &repeated,
1244 );
1245 }
1246 try std.testing.expectEqualDeep(final_root, replay.root());
1247 }
1248
1249 fn expectTransitionRejections(initial: fabric.Fabric) !void {
1250 var decoded_owner = initial;
1251 var first_reader = std.Io.Reader.fixed(&transition_streams[0]);
1252 const first = try fabric.transition.replayDisjoint(
1253 &decoded_owner,
1254 &first_reader,
1255 transition_roots[0],
1256 );
1257 var short_output: [fabric.transition.stream_bytes - 1]u8 = undefined;
1258 var short_writer = std.Io.Writer.fixed(&short_output);
1259 try std.testing.expectError(
1260 error.WriteFailed,
1261 fabric.transition.encodeDisjoint(&initial, &first, &short_writer),
1262 );
1263 var wrong = first;
1264 wrong.root = transition_roots[1];
1265 var output: [fabric.transition.stream_bytes]u8 = undefined;
1266 var wrong_writer = std.Io.Writer.fixed(&output);
1267 try std.testing.expectError(
1268 error.TransitionMismatch,
1269 fabric.transition.encodeDisjoint(&initial, &wrong, &wrong_writer),
1270 );
1271 try expectTransitionAliases(initial, first);
1272 try expectTransitionFraming(initial);
1273 try expectEveryTransitionByteBound(initial);
1274 }
1275
1276 fn expectFaultTransitionVariants(initial: fabric.Fabric) !void {
1277 try expectFaultTransitionRoundTrip(initial, .{
1278 .machine_crash = source_id,
1279 });
1280 try expectFaultTransitionRoundTrip(initial, .{ .process_crash = .{
1281 .node = source_id,
1282 .process = 7,
1283 } });
1284 try expectFaultTransitionRoundTrip(initial, .{ .partition = .{
1285 .first = source_id,
1286 .second = destination_id,
1287 .active = true,
1288 } });
1289 try expectFaultTransitionRoundTrip(initial, .{ .capacity_exhaustion = .{
1290 .resource = .packet,
1291 .active = true,
1292 } });
1293 var packet_ready = initial;
1294 for (0..2) |index| {
1295 var reader = std.Io.Reader.fixed(&transition_streams[index]);
1296 _ = try fabric.transition.replayDisjoint(
1297 &packet_ready,
1298 &reader,
1299 transition_roots[index],
1300 );
1301 }
1302 try expectFaultTransitionRoundTrip(packet_ready, .{ .packet_loss = 1 });
1303 try expectFaultTransitionRoundTrip(packet_ready, .{ .packet_delay = .{
1304 .packet = 1,
1305 .until_tick = 23,
1306 } });
1307 }
1308
1309 fn expectFaultTransitionRoundTrip(
1310 initial: fabric.Fabric,
1311 effect: fabric.FaultEffect,
1312 ) !void {
1313 var connected = initial;
1314 const faulted = try connected.applyFault(.{
1315 .effect = effect,
1316 .choice = .bypass,
1317 });
1318 const value: fabric.Transition = .{
1319 .entry = faulted.entry,
1320 .root = faulted.root,
1321 };
1322 var bytes: [fabric.transition.stream_bytes]u8 = undefined;
1323 var writer = std.Io.Writer.fixed(&bytes);
1324 try fabric.transition.encodeDisjoint(&initial, &value, &writer);
1325 var replay = initial;
1326 var reader = std.Io.Reader.fixed(&bytes);
1327 const reopened = try fabric.transition.replayDisjoint(
1328 &replay,
1329 &reader,
1330 faulted.root,
1331 );
1332 try std.testing.expectEqualDeep(value, reopened);
1333 try std.testing.expectEqualDeep(connected, replay);
1334 }
1335
1336 fn expectTransitionAliases(
1337 initial: fabric.Fabric,
1338 value: fabric.Transition,
1339 ) !void {
1340 var aliased_fabric = initial;
1341 var fabric_writer = std.Io.Writer.fixed(std.mem.asBytes(&aliased_fabric));
1342 try std.testing.expectError(
1343 error.SinkAliasesFabric,
1344 fabric.transition.encodeDisjoint(&aliased_fabric, &value, &fabric_writer),
1345 );
1346 var aliased_transition = value;
1347 var transition_writer = std.Io.Writer.fixed(std.mem.asBytes(&aliased_transition));
1348 try std.testing.expectError(
1349 error.SinkAliasesTransition,
1350 fabric.transition.encodeDisjoint(
1351 &initial,
1352 &aliased_transition,
1353 &transition_writer,
1354 ),
1355 );
1356 var source_alias = initial;
1357 var reader = std.Io.Reader.fixed(std.mem.asBytes(&source_alias));
1358 try std.testing.expectError(
1359 error.SourceAliasesFabric,
1360 fabric.transition.replayDisjoint(
1361 &source_alias,
1362 &reader,
1363 transition_roots[0],
1364 ),
1365 );
1366 try std.testing.expectEqualDeep(initial, source_alias);
1367 }
1368
1369 fn expectTransitionFraming(initial: fabric.Fabric) !void {
1370 try expectTransitionReplayFailure(
1371 error.TruncatedStream,
1372 initial,
1373 transition_streams[0][0 .. fabric.transition.stream_bytes - 1],
1374 transition_roots[0],
1375 );
1376 var trailing: [fabric.transition.stream_bytes + 1]u8 = undefined;
1377 @memcpy(trailing[0..fabric.transition.stream_bytes], &transition_streams[0]);
1378 trailing[fabric.transition.stream_bytes] = 0xa5;
1379 try expectTransitionReplayFailure(
1380 error.TrailingData,
1381 initial,
1382 &trailing,
1383 transition_roots[0],
1384 );
1385 try expectTransitionReplayFailure(
1386 error.ResultRootMismatch,
1387 initial,
1388 &transition_streams[0],
1389 transition_roots[1],
1390 );
1391 try expectTransitionReplayFailure(
1392 error.PreviousRootMismatch,
1393 initial,
1394 &transition_streams[1],
1395 transition_roots[1],
1396 );
1397 }
1398
1399 fn expectEveryTransitionByteBound(initial: fabric.Fabric) !void {
1400 var basis = initial;
1401 for (0..connected_transition_count) |index| {
1402 try expectEveryTransitionFrameByteBound(basis, index);
1403 var reader = std.Io.Reader.fixed(&transition_streams[index]);
1404 _ = try fabric.transition.replayDisjoint(
1405 &basis,
1406 &reader,
1407 transition_roots[index],
1408 );
1409 }
1410 }
1411
1412 fn expectEveryTransitionFrameByteBound(
1413 initial: fabric.Fabric,
1414 index: usize,
1415 ) !void {
1416 std.debug.assert(index < connected_transition_count);
1417 for (0..fabric.transition.stream_bytes) |offset| {
1418 transition_streams[index][offset] ^= 1;
1419 var replay = initial;
1420 var reader = std.Io.Reader.fixed(&transition_streams[index]);
1421 if (fabric.transition.replayDisjoint(
1422 &replay,
1423 &reader,
1424 transition_roots[index],
1425 )) |_| {
1426 transition_streams[index][offset] ^= 1;
1427 return error.TestAcceptedTransitionMutation;
1428 } else |_| {}
1429 try std.testing.expectEqualDeep(initial, replay);
1430 transition_streams[index][offset] ^= 1;
1431 }
1432 }
1433
1434 fn expectTransitionReplayFailure(
1435 expected: anyerror,
1436 initial: fabric.Fabric,
1437 bytes: []const u8,
1438 root: fabric.Root,
1439 ) !void {
1440 var replay = initial;
1441 var reader = std.Io.Reader.fixed(bytes);
1442 try std.testing.expectError(
1443 expected,
1444 fabric.transition.replayDisjoint(&replay, &reader, root),
1445 );
1446 try std.testing.expectEqualDeep(initial, replay);
1447 }
1448
1449 fn expectPacketPayloadCapacity(
1450 initial: fabric.Fabric,
1451 source: *const instance.Instance,
1452 fence: os.abi.ActivationFence,
1453 ) !void {
1454 const full_payload: [fabric.packet_bytes_max]u8 = @splat(0x5a);
1455 var full = initial;
1456 _ = try full.sendPacket(
1457 source_id,
1458 source,
1459 fence,
1460 .{ .destination = destination_id, .channel = 1, .bytes = &full_payload },
1461 );
1462 const oversized_payload: [fabric.packet_bytes_max + 1]u8 = @splat(0x5a);
1463 var oversized = initial;
1464 try std.testing.expectError(
1465 error.PayloadCapacityExceeded,
1466 oversized.sendPacket(
1467 source_id,
1468 source,
1469 fence,
1470 .{
1471 .destination = destination_id,
1472 .channel = 1,
1473 .bytes = &oversized_payload,
1474 },
1475 ),
1476 );
1477 }
1478
1479 fn expectPacketCapacityFault(
1480 initial: fabric.Fabric,
1481 source: *const instance.Instance,
1482 fence: os.abi.ActivationFence,
1483 ) !void {
1484 const effect: fabric.FaultEffect = .{ .capacity_exhaustion = .{
1485 .resource = .packet,
1486 .active = true,
1487 } };
1488 var bypass_branch = initial;
1489 const bypassed = try bypass_branch.applyFault(.{
1490 .effect = effect,
1491 .choice = .bypass,
1492 });
1493 var inject_branch = initial;
1494 const injected = try inject_branch.applyFault(.{
1495 .effect = effect,
1496 .choice = .inject,
1497 });
1498 try std.testing.expectEqualDeep(
1499 bypassed.decision.point,
1500 injected.decision.point,
1501 );
1502 _ = try bypass_branch.sendPacket(
1503 source_id,
1504 source,
1505 fence,
1506 .{ .destination = destination_id, .channel = 1, .bytes = "allowed" },
1507 );
1508 try std.testing.expectError(
1509 error.PacketCapacityExceeded,
1510 inject_branch.sendPacket(
1511 source_id,
1512 source,
1513 fence,
1514 .{ .destination = destination_id, .channel = 1, .bytes = "blocked" },
1515 ),
1516 );
1517 }
1518
1519 fn expectServiceBranching(
1520 initial: fabric.Fabric,
1521 source: *const instance.Instance,
1522 fence: os.abi.ActivationFence,
1523 ) !void {
1524 var service_ok = initial;
1525 const modeled_ok = try service_ok.settleService(
1526 source_id,
1527 source,
1528 fence,
1529 .{ .status = .ok, .output_root = @splat(0x91), .bytes = "modeled" },
1530 );
1531 var service_failed = initial;
1532 const modeled_failed = try service_failed.settleService(
1533 source_id,
1534 source,
1535 fence,
1536 .{ .status = .unavailable, .output_root = @splat(0x91), .bytes = "modeled" },
1537 );
1538 try expectDifferentRoots(modeled_ok.root, modeled_failed.root);
1539 }
1540
1541 fn expectPendingSafety(
1542 initial: fabric.Fabric,
1543 connected: *fabric.Fabric,
1544 destination: *const instance.Instance,
1545 source_fence: os.abi.ActivationFence,
1546 destination_fence: os.abi.ActivationFence,
1547 sent: fabric.Admitted,
1548 ) !void {
1549 try std.testing.expect(connected.turnPending());
1550 try std.testing.expectEqual(@as(u8, 1), connected.packetCount());
1551 try std.testing.expectEqualStrings("packet", (try connected.packet(1)).bytes());
1552 try std.testing.expectError(
1553 error.TurnPending,
1554 connected.receivePacket(destination_id, destination, destination_fence, 1),
1555 );
1556 var stale = initial;
1557 try std.testing.expectError(
1558 error.MachineBasisMismatch,
1559 stale.settleService(
1560 source_id,
1561 destination,
1562 destination_fence,
1563 .{ .status = .ok, .output_root = @splat(0x92), .bytes = "wrong" },
1564 ),
1565 );
1566 try expectInvalidReplays(initial, sent);
1567 try expectFenceRebinding(connected, source_fence, sent);
1568 }
1569
1570 fn expectInvalidReplays(initial: fabric.Fabric, sent: fabric.Admitted) !void {
1571 var changed_payload = sent.entry;
1572 changed_payload.value.admission.effect.packet_send.storage[0] ^= 1;
1573 var invalid_replay = initial;
1574 const invalid_root = invalid_replay.root();
1575 try std.testing.expectError(error.InvalidPacket, invalid_replay.replay(changed_payload));
1576 try std.testing.expectEqualDeep(invalid_root, invalid_replay.root());
1577 var changed_sequence = sent.entry;
1578 changed_sequence.sequence += 1;
1579 try std.testing.expectError(
1580 error.EntrySequenceMismatch,
1581 invalid_replay.replay(changed_sequence),
1582 );
1583 try std.testing.expectEqualDeep(invalid_root, invalid_replay.root());
1584 }
1585
1586 fn expectFenceRebinding(
1587 connected: *const fabric.Fabric,
1588 source_fence: os.abi.ActivationFence,
1589 sent: fabric.Admitted,
1590 ) !void {
1591 var rotated_fence = source_fence;
1592 rotated_fence.generation += 1;
1593 rotated_fence.token = @splat(0x52);
1594 const rebound = try connected.pendingDelivery(source_id, rotated_fence);
1595 try std.testing.expectEqualDeep(sent.delivery.admission, rebound.admission);
1596 try std.testing.expectEqualSlices(
1597 u8,
1598 &sent.delivery.next_source_root,
1599 &rebound.next_source_root,
1600 );
1601 try std.testing.expect(!std.meta.eql(sent.delivery.receipt, rebound.receipt));
1602 try std.testing.expectEqualDeep(sent.root, connected.root());
1603 }
1604
1605 fn expectReceiptDivergence(
1606 initial: fabric.Fabric,
1607 sent: fabric.Admitted,
1608 settled: fabric.Settled,
1609 ) !void {
1610 var changed = settled.entry;
1611 changed.value.settlement.receipt.semantic_transcript_digest[0] ^= 1;
1612 var replay = initial;
1613 _ = try replay.replay(sent.entry);
1614 try expectDifferentRoots(settled.root, try replay.replay(changed));
1615 }
1616
1617 fn snapshotFixture(
1618 first: *instance.Instance,
1619 second: *instance.Instance,
1620 receiver: *const instance.Instance,
1621 first_fence: os.abi.ActivationFence,
1622 second_fence: os.abi.ActivationFence,
1623 ) !fabric.Fabric {
1624 var connected = try readyPackets(
1625 first,
1626 second,
1627 receiver,
1628 first_fence,
1629 second_fence,
1630 );
1631 _ = try connected.applyFault(.{
1632 .effect = .{ .packet_delay = .{ .packet = 1, .until_tick = 12 } },
1633 .choice = .inject,
1634 });
1635 _ = try connected.applyFault(.{
1636 .effect = .{ .partition = .{
1637 .first = source_id,
1638 .second = receiver_id,
1639 .active = true,
1640 } },
1641 .choice = .inject,
1642 });
1643 _ = try connected.applyFault(.{
1644 .effect = .{ .capacity_exhaustion = .{
1645 .resource = .packet,
1646 .active = true,
1647 } },
1648 .choice = .inject,
1649 });
1650 _ = try connected.applyFault(.{
1651 .effect = .{ .machine_crash = destination_id },
1652 .choice = .inject,
1653 });
1654 return connected;
1655 }
1656
1657 fn expectSnapshotRoundTrip(connected: fabric.Fabric) !void {
1658 try encodeSnapshot(&connected);
1659 try std.testing.expectEqualSlices(u8, &.{
1660 'M', 'C', 'H', 'F', 'A', 'B', '1', 0,
1661 1, 0, 0, 32, 0, 0, 1, 0,
1662 3, 0, 4, 0, 15, 0, 208, 0,
1663 0, 1, 120, 0, 32, 0, 96, 0,
1664 160, 0,
1665 }, snapshot_stream[0..34]);
1666 var reader = std.Io.Reader.fixed(
1667 snapshot_stream[0..fabric.snapshot.stream_bytes],
1668 );
1669 const reopened = try fabric.snapshot.decodeDisjoint(
1670 &reader,
1671 connected.root(),
1672 );
1673 try std.testing.expectEqualDeep(try connected.cut(), try reopened.cut());
1674 try std.testing.expectEqualStrings("first", (try reopened.packet(1)).bytes());
1675 try std.testing.expectEqualStrings("second", (try reopened.packet(2)).bytes());
1676 try std.testing.expect(try reopened.partitioned(source_id, receiver_id));
1677 try std.testing.expect(!try reopened.nodeAvailable(destination_id));
1678 var repeated: [fabric.snapshot.stream_bytes]u8 = undefined;
1679 var writer = std.Io.Writer.fixed(&repeated);
1680 try fabric.snapshot.encodeDisjoint(&reopened, &writer);
1681 try std.testing.expectEqual(fabric.snapshot.stream_bytes, writer.end);
1682 try std.testing.expectEqualSlices(
1683 u8,
1684 snapshot_stream[0..fabric.snapshot.stream_bytes],
1685 &repeated,
1686 );
1687 }
1688
1689 fn expectSnapshotRejections(
1690 initial: fabric.Fabric,
1691 machine: *const instance.Instance,
1692 fence: os.abi.ActivationFence,
1693 ) !void {
1694 try encodeSnapshot(&initial);
1695 var short_writer = std.Io.Writer.fixed(
1696 snapshot_stream[0 .. fabric.snapshot.stream_bytes - 1],
1697 );
1698 try std.testing.expectError(
1699 error.WriteFailed,
1700 fabric.snapshot.encodeDisjoint(&initial, &short_writer),
1701 );
1702 try expectSnapshotDecodeFailure(
1703 error.TruncatedStream,
1704 snapshot_stream[0 .. fabric.snapshot.stream_bytes - 1],
1705 initial.root(),
1706 );
1707 snapshot_stream[fabric.snapshot.stream_bytes] = 0xa5;
1708 try expectSnapshotDecodeFailure(error.TrailingData, &snapshot_stream, initial.root());
1709 try expectSnapshotMutations(initial.root());
1710 try expectEverySnapshotByteBound(initial.root());
1711 var wrong_root = initial.root();
1712 wrong_root.digest[0] ^= 1;
1713 try expectSnapshotDecodeFailure(
1714 error.FabricRootMismatch,
1715 snapshot_stream[0..fabric.snapshot.stream_bytes],
1716 wrong_root,
1717 );
1718 var pending = initial;
1719 _ = try pending.sendPacket(
1720 source_id,
1721 machine,
1722 fence,
1723 .{ .destination = destination_id, .channel = 1, .bytes = "pending" },
1724 );
1725 var pending_writer = std.Io.Writer.fixed(
1726 snapshot_stream[0..fabric.snapshot.stream_bytes],
1727 );
1728 try std.testing.expectError(
1729 error.TurnPending,
1730 fabric.snapshot.encodeDisjoint(&pending, &pending_writer),
1731 );
1732 try std.testing.expectEqual(@as(usize, 0), pending_writer.end);
1733 var aliased = initial;
1734 var alias_writer = std.Io.Writer.fixed(std.mem.asBytes(&aliased));
1735 try std.testing.expectError(
1736 error.SinkAliasesFabric,
1737 fabric.snapshot.encodeDisjoint(&aliased, &alias_writer),
1738 );
1739 }
1740
1741 fn expectSnapshotMutations(expected_root: fabric.Root) !void {
1742 const mutations = [_]SnapshotMutation{
1743 .{ .offset = 0, .mask = 1, .expected = error.BadMagic },
1744 .{ .offset = 8, .mask = 1, .expected = error.UnsupportedVersion },
1745 .{ .offset = 10, .mask = 1, .expected = error.StreamBytesMismatch },
1746 .{ .offset = 12, .mask = 1, .expected = error.UnsupportedFlags },
1747 .{ .offset = 14, .mask = 1, .expected = error.SnapshotFormatMismatch },
1748 .{ .offset = 16, .mask = 1, .expected = error.UnknownSnapshotField },
1749 .{ .offset = 18, .mask = 1, .expected = error.MetadataShapeMismatch },
1750 .{ .offset = 34, .mask = 1, .expected = error.ReservedNonzero },
1751 .{ .offset = 154, .mask = 1, .expected = error.ReservedNonzero },
1752 .{ .offset = 180, .mask = 1, .expected = error.ReservedNonzero },
1753 .{ .offset = 440, .mask = 3, .expected = error.NonCanonicalBoolean },
1754 .{ .offset = 488, .mask = 0xff, .expected = error.UnknownSnapshotField },
1755 .{ .offset = 736, .mask = 1, .expected = error.ReservedNonzero },
1756 .{ .offset = 1152, .mask = 1, .expected = error.ReservedNonzero },
1757 .{ .offset = 4992, .mask = 1, .expected = error.ReservedNonzero },
1758 .{ .offset = 160, .mask = 1, .expected = error.RootMismatch },
1759 };
1760 for (mutations) |mutation| {
1761 snapshot_stream[mutation.offset] ^= mutation.mask;
1762 try expectSnapshotDecodeFailure(
1763 mutation.expected,
1764 snapshot_stream[0..fabric.snapshot.stream_bytes],
1765 expected_root,
1766 );
1767 snapshot_stream[mutation.offset] ^= mutation.mask;
1768 }
1769 }
1770
1771 fn expectEverySnapshotByteBound(expected_root: fabric.Root) !void {
1772 for (0..fabric.snapshot.stream_bytes) |offset| {
1773 snapshot_stream[offset] ^= 1;
1774 var reader = std.Io.Reader.fixed(
1775 snapshot_stream[0..fabric.snapshot.stream_bytes],
1776 );
1777 if (fabric.snapshot.decodeDisjoint(&reader, expected_root)) |_| {
1778 snapshot_stream[offset] ^= 1;
1779 return error.TestAcceptedSnapshotMutation;
1780 } else |_| {}
1781 snapshot_stream[offset] ^= 1;
1782 }
1783 }
1784
1785 fn encodeSnapshot(value: *const fabric.Fabric) !void {
1786 var writer = std.Io.Writer.fixed(
1787 snapshot_stream[0..fabric.snapshot.stream_bytes],
1788 );
1789 try fabric.snapshot.encodeDisjoint(value, &writer);
1790 try std.testing.expectEqual(fabric.snapshot.stream_bytes, writer.end);
1791 }
1792
1793 fn expectSnapshotDecodeFailure(
1794 expected: anyerror,
1795 bytes: []const u8,
1796 root: fabric.Root,
1797 ) !void {
1798 var reader = std.Io.Reader.fixed(bytes);
1799 try std.testing.expectError(
1800 expected,
1801 fabric.snapshot.decodeDisjoint(&reader, root),
1802 );
1803 }
1804
1805 fn start(
1806 storage: *instance.Storage,
1807 ram: []align(instance.ram_alignment) u8,
1808 input: instance.Input,
1809 ) !instance.Instance {
1810 var machine = switch (instance.Instance.init(storage, ram, input)) {
1811 .ready => |value| value,
1812 .unavailable => return error.UnexpectedBackendUnavailable,
1813 .rejected => |failure| return failure,
1814 };
1815 try activate(&machine);
1816 return machine;
1817 }
1818
1819 fn activate(machine: *instance.Instance) !void {
1820 try std.testing.expectEqualDeep(
1821 instance.Exit{ .doorbell = .{ .code = .ready } },
1822 try machine.run(),
1823 );
1824 var events: instance.EventBatch = undefined;
1825 try machine.takeEvents(&events);
1826 try std.testing.expectEqual(instance.RunPhase.awaiting_input, machine.phase());
1827 }
1828
1829 fn completeTurn(
1830 machine: *instance.Instance,
1831 delivery: admission.Delivery,
1832 ) !instance.QuiescenceReceipt {
1833 try machine.deliverAdmitted(&delivery);
1834 try std.testing.expectEqualDeep(
1835 instance.Exit{ .doorbell = .{ .code = .quiescent } },
1836 try machine.run(),
1837 );
1838 var events: instance.EventBatch = undefined;
1839 try machine.takeEvents(&events);
1840 try machine.acknowledge(delivery.receipt);
1841 return machine.quiescenceReceipt();
1842 }
1843
1844 fn machineInput(
1845 execution: os.boot.kernel.manifest.View,
1846 fence: os.abi.ActivationFence,
1847 root_byte: u8,
1848 outstanding_effect: ?admission.EffectRequest,
1849 ) instance.Input {
1850 return .{
1851 .profile = profile.interpretedContinuationTestV1(),
1852 .elf = k0_elf,
1853 .execution_manifest = k0_manifest,
1854 .expected_execution_fingerprint = execution.header.fingerprint,
1855 .fence = fence,
1856 .initial_time_tick = 7,
1857 .entropy_generation = 1,
1858 .terminal_offset = 0,
1859 .effect_frontier = 0,
1860 .block_root = @splat(root_byte),
1861 .source_root = @splat(root_byte + 1),
1862 .input_frontier = 0,
1863 .terminal_input_offset = 0,
1864 .outstanding_effect = outstanding_effect,
1865 };
1866 }
1867
1868 fn activationFence(generation: u64, token: u8) os.abi.ActivationFence {
1869 return .{ .world = world, .generation = generation, .token = @splat(token) };
1870 }
1871
1872 fn effectRequest(byte: u8) admission.EffectRequest {
1873 return .{
1874 .receipt = .{ .digest = @splat(byte) },
1875 .correlation = 1,
1876 };
1877 }
1878
1879 fn expectInitError(
1880 expected: fabric.Error,
1881 selected_world: [16]u8,
1882 tick: u64,
1883 nodes: []const fabric.Node,
1884 ) !void {
1885 try std.testing.expectError(expected, fabric.Fabric.init(.{
1886 .profile = profile.interpretedContinuationTestV1(),
1887 .world = selected_world,
1888 .virtual_time_tick = tick,
1889 .nodes = nodes,
1890 }));
1891 }
1892
1893 fn expectDifferentRoots(left: fabric.Root, right: fabric.Root) !void {
1894 try std.testing.expect(!std.mem.eql(u8, &left.digest, &right.digest));
1895 }
1896
1897 fn expectRootHex(root: fabric.Root, expected: []const u8) !void {
1898 const actual = std.fmt.bytesToHex(root.digest, .lower);
1899 try std.testing.expectEqualStrings(expected, &actual);
1900 }