tigerbeetle-node 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +302 -101
  2. package/dist/index.d.ts +70 -72
  3. package/dist/index.js +70 -72
  4. package/dist/index.js.map +1 -1
  5. package/package.json +9 -8
  6. package/scripts/download_node_headers.sh +14 -7
  7. package/src/index.ts +6 -10
  8. package/src/node.zig +6 -3
  9. package/src/tigerbeetle/scripts/benchmark.sh +4 -4
  10. package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
  11. package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
  12. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
  13. package/src/tigerbeetle/scripts/install.sh +19 -4
  14. package/src/tigerbeetle/scripts/install_zig.bat +5 -1
  15. package/src/tigerbeetle/scripts/install_zig.sh +24 -14
  16. package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
  17. package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
  18. package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
  19. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
  20. package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
  21. package/src/tigerbeetle/src/benchmark.zig +29 -13
  22. package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
  23. package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
  24. package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
  25. package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
  26. package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -257
  27. package/src/tigerbeetle/src/c/tb_client.h +118 -84
  28. package/src/tigerbeetle/src/c/tb_client.zig +88 -23
  29. package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
  30. package/src/tigerbeetle/src/c/test.zig +371 -1
  31. package/src/tigerbeetle/src/cli.zig +37 -7
  32. package/src/tigerbeetle/src/config.zig +58 -17
  33. package/src/tigerbeetle/src/demo.zig +5 -2
  34. package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
  35. package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
  36. package/src/tigerbeetle/src/ewah.zig +11 -33
  37. package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
  38. package/src/tigerbeetle/src/io/linux.zig +1 -1
  39. package/src/tigerbeetle/src/lsm/README.md +308 -0
  40. package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
  41. package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
  42. package/src/tigerbeetle/src/lsm/compaction.zig +376 -397
  43. package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
  44. package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
  45. package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
  46. package/src/tigerbeetle/src/lsm/forest.zig +21 -447
  47. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +414 -0
  48. package/src/tigerbeetle/src/lsm/grid.zig +170 -76
  49. package/src/tigerbeetle/src/lsm/groove.zig +197 -133
  50. package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
  51. package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
  52. package/src/tigerbeetle/src/lsm/manifest.zig +93 -180
  53. package/src/tigerbeetle/src/lsm/manifest_level.zig +161 -454
  54. package/src/tigerbeetle/src/lsm/manifest_log.zig +243 -356
  55. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
  56. package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
  57. package/src/tigerbeetle/src/lsm/posted_groove.zig +65 -76
  58. package/src/tigerbeetle/src/lsm/segmented_array.zig +580 -251
  59. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
  60. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
  61. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
  62. package/src/tigerbeetle/src/lsm/table.zig +115 -68
  63. package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
  64. package/src/tigerbeetle/src/lsm/table_iterator.zig +27 -17
  65. package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
  66. package/src/tigerbeetle/src/lsm/test.zig +61 -56
  67. package/src/tigerbeetle/src/lsm/tree.zig +450 -407
  68. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +461 -0
  69. package/src/tigerbeetle/src/main.zig +83 -8
  70. package/src/tigerbeetle/src/message_bus.zig +20 -9
  71. package/src/tigerbeetle/src/message_pool.zig +22 -19
  72. package/src/tigerbeetle/src/ring_buffer.zig +7 -3
  73. package/src/tigerbeetle/src/simulator.zig +179 -119
  74. package/src/tigerbeetle/src/state_machine.zig +381 -246
  75. package/src/tigerbeetle/src/static_allocator.zig +65 -0
  76. package/src/tigerbeetle/src/storage.zig +3 -7
  77. package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
  78. package/src/tigerbeetle/src/test/accounting/workload.zig +823 -0
  79. package/src/tigerbeetle/src/test/cluster.zig +33 -81
  80. package/src/tigerbeetle/src/test/conductor.zig +366 -0
  81. package/src/tigerbeetle/src/test/fuzz.zig +121 -0
  82. package/src/tigerbeetle/src/test/id.zig +89 -0
  83. package/src/tigerbeetle/src/test/network.zig +45 -19
  84. package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
  85. package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
  86. package/src/tigerbeetle/src/test/state_checker.zig +91 -69
  87. package/src/tigerbeetle/src/test/state_machine.zig +11 -35
  88. package/src/tigerbeetle/src/test/storage.zig +470 -106
  89. package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
  90. package/src/tigerbeetle/src/tigerbeetle.zig +15 -16
  91. package/src/tigerbeetle/src/unit_tests.zig +13 -1
  92. package/src/tigerbeetle/src/util.zig +97 -11
  93. package/src/tigerbeetle/src/vopr.zig +495 -0
  94. package/src/tigerbeetle/src/vsr/client.zig +21 -3
  95. package/src/tigerbeetle/src/vsr/journal.zig +293 -212
  96. package/src/tigerbeetle/src/vsr/replica.zig +1086 -515
  97. package/src/tigerbeetle/src/vsr/superblock.zig +382 -637
  98. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +14 -16
  99. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +416 -153
  100. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
  101. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
  102. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +62 -12
  103. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
  104. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
  105. package/src/tigerbeetle/src/vsr.zig +94 -60
  106. package/src/tigerbeetle/scripts/vopr.bat +0 -48
  107. package/src/tigerbeetle/scripts/vopr.sh +0 -33
  108. package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
  109. package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
@@ -5,6 +5,7 @@ const mem = std.mem;
5
5
  const log = std.log.scoped(.state_machine);
6
6
 
7
7
  const tb = @import("tigerbeetle.zig");
8
+ const snapshot_latest = @import("lsm/tree.zig").snapshot_latest;
8
9
 
9
10
  const Account = tb.Account;
10
11
  const AccountFlags = tb.AccountFlags;
@@ -18,7 +19,9 @@ const CreateTransfersResult = tb.CreateTransfersResult;
18
19
  const CreateAccountResult = tb.CreateAccountResult;
19
20
  const CreateTransferResult = tb.CreateTransferResult;
20
21
 
21
- pub fn StateMachineType(comptime Storage: type) type {
22
+ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
23
+ message_body_size_max: usize,
24
+ }) type {
22
25
  return struct {
23
26
  const StateMachine = @This();
24
27
 
@@ -63,11 +66,35 @@ pub fn StateMachineType(comptime Storage: type) type {
63
66
  lookup_transfers,
64
67
  };
65
68
 
69
+ pub const constants = struct {
70
+ /// The maximum number of objects within a batch, by operation.
71
+ pub const batch_max = struct {
72
+ pub const create_accounts = operation_batch_max(.create_accounts);
73
+ pub const create_transfers = operation_batch_max(.create_transfers);
74
+ pub const lookup_accounts = operation_batch_max(.lookup_accounts);
75
+ pub const lookup_transfers = operation_batch_max(.lookup_transfers);
76
+
77
+ comptime {
78
+ assert(create_accounts > 0);
79
+ assert(create_transfers > 0);
80
+ assert(lookup_accounts > 0);
81
+ assert(lookup_transfers > 0);
82
+ }
83
+
84
+ fn operation_batch_max(comptime operation: Operation) usize {
85
+ return @divFloor(constants_.message_body_size_max, std.math.max(
86
+ @sizeOf(Event(operation)),
87
+ @sizeOf(Result(operation)),
88
+ ));
89
+ }
90
+ };
91
+ };
92
+
66
93
  pub const Options = struct {
67
94
  lsm_forest_node_count: u32,
68
- cache_size_accounts: u32,
69
- cache_size_transfers: u32,
70
- cache_size_posted: u32,
95
+ cache_entries_accounts: u32,
96
+ cache_entries_transfers: u32,
97
+ cache_entries_posted: u32,
71
98
  };
72
99
 
73
100
  prepare_timestamp: u64,
@@ -92,20 +119,7 @@ pub fn StateMachineType(comptime Storage: type) type {
92
119
  allocator,
93
120
  grid,
94
121
  options.lsm_forest_node_count,
95
- .{
96
- .accounts = .{
97
- .cache_size = options.cache_size_accounts,
98
- .commit_count_max = 8191,
99
- },
100
- .transfers = .{
101
- .cache_size = options.cache_size_transfers,
102
- .commit_count_max = 8191 * 2,
103
- },
104
- .posted = .{
105
- .cache_size = options.cache_size_posted,
106
- .commit_count_max = 8191 * 2,
107
- },
108
- },
122
+ forest_options(options),
109
123
  );
110
124
  errdefer forest.deinit(allocator);
111
125
 
@@ -207,11 +221,10 @@ pub fn StateMachineType(comptime Storage: type) type {
207
221
  self.prefetch_input = input;
208
222
  self.prefetch_callback = callback;
209
223
 
210
- // We do this here instead of at the end of commit() to avoid the need to call
211
- // prefetch() in the StateMachine unit tests below.
212
- self.forest.grooves.accounts.prefetch_clear();
213
- self.forest.grooves.transfers.prefetch_clear();
214
- self.forest.grooves.posted.prefetch_clear();
224
+ // TODO(Snapshots) Pass in the target snapshot.
225
+ self.forest.grooves.accounts.prefetch_setup(null);
226
+ self.forest.grooves.transfers.prefetch_setup(null);
227
+ self.forest.grooves.posted.prefetch_setup(null);
215
228
 
216
229
  return switch (operation) {
217
230
  .reserved, .root, .register => unreachable,
@@ -348,11 +361,11 @@ pub fn StateMachineType(comptime Storage: type) type {
348
361
  client: u128,
349
362
  op: u64,
350
363
  operation: Operation,
351
- input: []const u8,
352
- output: []u8,
364
+ input: []align(16) const u8,
365
+ output: []align(16) u8,
353
366
  ) usize {
354
367
  _ = client;
355
- _ = op;
368
+ assert(op != 0);
356
369
 
357
370
  const result = switch (operation) {
358
371
  .root => unreachable,
@@ -367,10 +380,6 @@ pub fn StateMachineType(comptime Storage: type) type {
367
380
  return result;
368
381
  }
369
382
 
370
- pub fn tick(self: *StateMachine) void {
371
- self.forest.tick();
372
- }
373
-
374
383
  pub fn compact(self: *StateMachine, callback: fn (*StateMachine) void, op: u64) void {
375
384
  assert(self.compact_callback == null);
376
385
  assert(self.checkpoint_callback == null);
@@ -404,8 +413,8 @@ pub fn StateMachineType(comptime Storage: type) type {
404
413
  fn execute(
405
414
  self: *StateMachine,
406
415
  comptime operation: Operation,
407
- input: []const u8,
408
- output: []u8,
416
+ input: []align(16) const u8,
417
+ output: []align(16) u8,
409
418
  ) usize {
410
419
  comptime assert(operation != .lookup_accounts and operation != .lookup_transfers);
411
420
 
@@ -417,14 +426,21 @@ pub fn StateMachineType(comptime Storage: type) type {
417
426
  var chain_broken = false;
418
427
 
419
428
  for (events) |*event, index| {
420
- if (event.flags.linked and chain == null) {
421
- chain = index;
422
- assert(chain_broken == false);
423
- }
424
- const result = if (chain_broken) .linked_event_failed else switch (operation) {
425
- .create_accounts => self.create_account(event),
426
- .create_transfers => self.create_transfer(event),
427
- else => unreachable,
429
+ const result = blk: {
430
+ if (event.flags.linked) {
431
+ if (chain == null) {
432
+ chain = index;
433
+ assert(chain_broken == false);
434
+ }
435
+
436
+ if (index == events.len - 1) break :blk .linked_event_chain_open;
437
+ }
438
+
439
+ break :blk if (chain_broken) .linked_event_failed else switch (operation) {
440
+ .create_accounts => self.create_account(event),
441
+ .create_transfers => self.create_transfer(event),
442
+ else => unreachable,
443
+ };
428
444
  };
429
445
  log.debug("{s} {}/{}: {}: {}", .{
430
446
  @tagName(operation),
@@ -449,19 +465,17 @@ pub fn StateMachineType(comptime Storage: type) type {
449
465
  count += 1;
450
466
  }
451
467
  } else {
452
- assert(result == .linked_event_failed);
468
+ assert(result == .linked_event_failed or result == .linked_event_chain_open);
453
469
  }
454
470
  }
455
471
  results[count] = .{ .index = @intCast(u32, index), .result = result };
456
472
  count += 1;
457
473
  }
458
- if (!event.flags.linked and chain != null) {
474
+ if (chain != null and (!event.flags.linked or result == .linked_event_chain_open)) {
459
475
  chain = null;
460
476
  chain_broken = false;
461
477
  }
462
478
  }
463
- // TODO client.zig: Validate that batch chains are always well-formed and closed.
464
- // This is programming error and we should raise an exception for this in the client ASAP.
465
479
  assert(chain == null);
466
480
  assert(chain_broken == false);
467
481
 
@@ -505,6 +519,7 @@ pub fn StateMachineType(comptime Storage: type) type {
505
519
  assert(index == chain_start_index);
506
520
  }
507
521
 
522
+ // Accounts that do not fit in the response are omitted.
508
523
  fn execute_lookup_accounts(self: *StateMachine, input: []const u8, output: []u8) usize {
509
524
  const batch = mem.bytesAsSlice(u128, input);
510
525
  const output_len = @divFloor(output.len, @sizeOf(Account)) * @sizeOf(Account);
@@ -519,6 +534,7 @@ pub fn StateMachineType(comptime Storage: type) type {
519
534
  return results_count * @sizeOf(Account);
520
535
  }
521
536
 
537
+ // Transfers that do not fit in the response are omitted.
522
538
  fn execute_lookup_transfers(self: *StateMachine, input: []const u8, output: []u8) usize {
523
539
  const batch = mem.bytesAsSlice(u128, input);
524
540
  const output_len = @divFloor(output.len, @sizeOf(Transfer)) * @sizeOf(Transfer);
@@ -548,16 +564,14 @@ pub fn StateMachineType(comptime Storage: type) type {
548
564
  return .mutually_exclusive_flags;
549
565
  }
550
566
 
551
- if (sum_overflows(a.debits_pending, a.debits_posted)) return .overflows_debits;
552
- if (sum_overflows(a.credits_pending, a.credits_posted)) return .overflows_credits;
553
-
554
- // Opening balances may never exceed limits:
555
- if (a.debits_exceed_credits(0)) return .exceeds_credits;
556
- if (a.credits_exceed_debits(0)) return .exceeds_debits;
567
+ if (a.debits_pending != 0) return .debits_pending_must_be_zero;
568
+ if (a.debits_posted != 0) return .debits_posted_must_be_zero;
569
+ if (a.credits_pending != 0) return .credits_pending_must_be_zero;
570
+ if (a.credits_posted != 0) return .credits_posted_must_be_zero;
557
571
 
558
572
  if (self.get_account(a.id)) |e| return create_account_exists(a, e);
559
573
 
560
- self.forest.grooves.accounts.put(a);
574
+ self.forest.grooves.accounts.put_no_clobber(a);
561
575
 
562
576
  self.commit_timestamp = a.timestamp;
563
577
  return .ok;
@@ -574,10 +588,6 @@ pub fn StateMachineType(comptime Storage: type) type {
574
588
  assert(zeroed_48_bytes(a.reserved) and zeroed_48_bytes(e.reserved));
575
589
  if (a.ledger != e.ledger) return .exists_with_different_ledger;
576
590
  if (a.code != e.code) return .exists_with_different_code;
577
- if (a.debits_pending != e.debits_pending) return .exists_with_different_debits_pending;
578
- if (a.debits_posted != e.debits_posted) return .exists_with_different_debits_posted;
579
- if (a.credits_pending != e.credits_pending) return .exists_with_different_credits_pending;
580
- if (a.credits_posted != e.credits_posted) return .exists_with_different_credits_posted;
581
591
  return .exists;
582
592
  }
583
593
 
@@ -643,6 +653,7 @@ pub fn StateMachineType(comptime Storage: type) type {
643
653
  if (sum_overflows(t.amount, cr.credits_pending + cr.credits_posted)) {
644
654
  return .overflows_credits;
645
655
  }
656
+ if (sum_overflows(t.timestamp, t.timeout)) return .overflows_timeout;
646
657
 
647
658
  if (dr.debits_exceed_credits(t.amount)) return .exceeds_credits;
648
659
  if (cr.credits_exceed_debits(t.amount)) return .exceeds_debits;
@@ -899,6 +910,91 @@ pub fn StateMachineType(comptime Storage: type) type {
899
910
  fn get_posted(self: *const StateMachine, pending_id: u128) ?bool {
900
911
  return self.forest.grooves.posted.get(pending_id);
901
912
  }
913
+
914
+ pub fn forest_options(options: Options) Forest.GroovesOptions {
915
+ const batch_accounts_max = @intCast(u32, constants.batch_max.create_accounts);
916
+ const batch_transfers_max = @intCast(u32, constants.batch_max.create_transfers);
917
+ assert(batch_accounts_max == constants.batch_max.lookup_accounts);
918
+ assert(batch_transfers_max == constants.batch_max.lookup_transfers);
919
+
920
+ return .{
921
+ .accounts = .{
922
+ .cache_entries_max = options.cache_entries_accounts,
923
+ .prefetch_entries_max = std.math.max(
924
+ // create_account()/lookup_account() looks up 1 account per item.
925
+ batch_accounts_max,
926
+ // create_transfer()/post_or_void_pending_transfer() looks up 2
927
+ // accounts for every transfer.
928
+ 2 * batch_transfers_max,
929
+ ),
930
+ .tree_options_object = .{
931
+ .commit_entries_max = math.max(
932
+ batch_accounts_max,
933
+ // ×2 because creating a transfer will update 2 accounts.
934
+ 2 * batch_transfers_max,
935
+ ),
936
+ },
937
+ .tree_options_id = .{ .commit_entries_max = batch_accounts_max },
938
+ .tree_options_index = .{
939
+ .user_data = .{ .commit_entries_max = batch_accounts_max },
940
+ .ledger = .{ .commit_entries_max = batch_accounts_max },
941
+ .code = .{ .commit_entries_max = batch_accounts_max },
942
+ // Transfers mutate the secondary indices for debits/credits pending/posted.
943
+ //
944
+ // * Each mutation results in a remove and an insert: the ×2 multiplier.
945
+ // * Each transfer modifies two accounts. However, this does not
946
+ // necessitate an additional ×2 multiplier — the credits of the debit
947
+ // account and the debits of the credit account are not modified.
948
+ .debits_pending = .{
949
+ .commit_entries_max = math.max(
950
+ batch_accounts_max,
951
+ 2 * batch_transfers_max,
952
+ ),
953
+ },
954
+ .debits_posted = .{
955
+ .commit_entries_max = math.max(
956
+ batch_accounts_max,
957
+ 2 * batch_transfers_max,
958
+ ),
959
+ },
960
+ .credits_pending = .{
961
+ .commit_entries_max = math.max(
962
+ batch_accounts_max,
963
+ 2 * batch_transfers_max,
964
+ ),
965
+ },
966
+ .credits_posted = .{
967
+ .commit_entries_max = math.max(
968
+ batch_accounts_max,
969
+ 2 * batch_transfers_max,
970
+ ),
971
+ },
972
+ },
973
+ },
974
+ .transfers = .{
975
+ .cache_entries_max = options.cache_entries_transfers,
976
+ // *2 to fetch pending and post/void transfer.
977
+ .prefetch_entries_max = 2 * batch_transfers_max,
978
+ .tree_options_object = .{ .commit_entries_max = batch_transfers_max },
979
+ .tree_options_id = .{ .commit_entries_max = batch_transfers_max },
980
+ .tree_options_index = .{
981
+ .debit_account_id = .{ .commit_entries_max = batch_transfers_max },
982
+ .credit_account_id = .{ .commit_entries_max = batch_transfers_max },
983
+ .user_data = .{ .commit_entries_max = batch_transfers_max },
984
+ .pending_id = .{ .commit_entries_max = batch_transfers_max },
985
+ .timeout = .{ .commit_entries_max = batch_transfers_max },
986
+ .ledger = .{ .commit_entries_max = batch_transfers_max },
987
+ .code = .{ .commit_entries_max = batch_transfers_max },
988
+ .amount = .{ .commit_entries_max = batch_transfers_max },
989
+ },
990
+ },
991
+ .posted = .{
992
+ .cache_entries_max = options.cache_entries_posted,
993
+ .prefetch_entries_max = batch_transfers_max,
994
+ .commit_entries_max = batch_transfers_max,
995
+ },
996
+ };
997
+ }
902
998
  };
903
999
  }
904
1000
 
@@ -962,7 +1058,11 @@ const TestContext = struct {
962
1058
  const MessagePool = @import("message_pool.zig").MessagePool;
963
1059
  const SuperBlock = @import("vsr/superblock.zig").SuperBlockType(Storage);
964
1060
  const Grid = @import("lsm/grid.zig").GridType(Storage);
965
- const StateMachine = StateMachineType(Storage);
1061
+ const StateMachine = StateMachineType(Storage, .{
1062
+ // Overestimate the batch size (in order to overprovision commit_entries_max)
1063
+ // because the test never compacts.
1064
+ .message_body_size_max = 1000 * @sizeOf(Account),
1065
+ });
966
1066
 
967
1067
  storage: Storage,
968
1068
  message_pool: MessagePool,
@@ -970,36 +1070,41 @@ const TestContext = struct {
970
1070
  grid: Grid,
971
1071
  state_machine: StateMachine,
972
1072
 
973
- fn init(ctx: *TestContext, allocator: mem.Allocator, options: StateMachine.Options) !void {
1073
+ fn init(ctx: *TestContext, allocator: mem.Allocator) !void {
974
1074
  ctx.storage = try Storage.init(
975
1075
  allocator,
976
1076
  4096,
977
1077
  .{
978
- .seed = 0,
979
1078
  .read_latency_min = 0,
980
1079
  .read_latency_mean = 0,
981
1080
  .write_latency_min = 0,
982
1081
  .write_latency_mean = 0,
983
- .read_fault_probability = 0,
984
- .write_fault_probability = 0,
985
- },
986
- 0,
987
- .{
988
- .first_offset = 0,
989
- .period = 0,
990
1082
  },
991
1083
  );
992
1084
  errdefer ctx.storage.deinit(allocator);
993
1085
 
994
- ctx.message_pool = .{ .free_list = null };
1086
+ ctx.message_pool = .{
1087
+ .free_list = null,
1088
+ .messages_max = 0,
1089
+ };
1090
+ errdefer ctx.message_pool.deinit(allocator);
995
1091
 
996
1092
  ctx.superblock = try SuperBlock.init(allocator, &ctx.storage, &ctx.message_pool);
997
1093
  errdefer ctx.superblock.deinit(allocator);
998
1094
 
1095
+ // Pretend that the superblock is open so that the Forest can initialize.
1096
+ ctx.superblock.opened = true;
1097
+ ctx.superblock.working.vsr_state.commit_min = 0;
1098
+
999
1099
  ctx.grid = try Grid.init(allocator, &ctx.superblock);
1000
1100
  errdefer ctx.grid.deinit(allocator);
1001
1101
 
1002
- ctx.state_machine = try StateMachine.init(allocator, &ctx.grid, options);
1102
+ ctx.state_machine = try StateMachine.init(allocator, &ctx.grid, .{
1103
+ .lsm_forest_node_count = 1,
1104
+ .cache_entries_accounts = 2048,
1105
+ .cache_entries_transfers = 2048,
1106
+ .cache_entries_posted = 2048,
1107
+ });
1003
1108
  errdefer ctx.state_machine.deinit(allocator);
1004
1109
  }
1005
1110
 
@@ -1024,10 +1129,6 @@ test "create/lookup/rollback accounts" {
1024
1129
  .user_data = 2,
1025
1130
  .ledger = 3,
1026
1131
  .code = 4,
1027
- .debits_pending = 5,
1028
- .debits_posted = 6,
1029
- .credits_pending = 7,
1030
- .credits_posted = 8,
1031
1132
  .timestamp = 1,
1032
1133
  }),
1033
1134
  },
@@ -1044,10 +1145,10 @@ test "create/lookup/rollback accounts" {
1044
1145
  .debits_must_not_exceed_credits = true,
1045
1146
  .credits_must_not_exceed_debits = true,
1046
1147
  },
1047
- .debits_pending = math.maxInt(u64),
1048
- .debits_posted = math.maxInt(u64),
1049
- .credits_pending = math.maxInt(u64),
1050
- .credits_posted = math.maxInt(u64),
1148
+ .debits_pending = 1,
1149
+ .debits_posted = 1,
1150
+ .credits_pending = 1,
1151
+ .credits_posted = 1,
1051
1152
  .timestamp = 2,
1052
1153
  }),
1053
1154
  },
@@ -1064,10 +1165,10 @@ test "create/lookup/rollback accounts" {
1064
1165
  .debits_must_not_exceed_credits = true,
1065
1166
  .credits_must_not_exceed_debits = true,
1066
1167
  },
1067
- .debits_pending = math.maxInt(u64),
1068
- .debits_posted = math.maxInt(u64),
1069
- .credits_pending = math.maxInt(u64),
1070
- .credits_posted = math.maxInt(u64),
1168
+ .debits_pending = 1,
1169
+ .debits_posted = 1,
1170
+ .credits_pending = 1,
1171
+ .credits_posted = 1,
1071
1172
  .timestamp = 2,
1072
1173
  }),
1073
1174
  },
@@ -1083,10 +1184,10 @@ test "create/lookup/rollback accounts" {
1083
1184
  .debits_must_not_exceed_credits = true,
1084
1185
  .credits_must_not_exceed_debits = true,
1085
1186
  },
1086
- .debits_pending = math.maxInt(u64),
1087
- .debits_posted = math.maxInt(u64),
1088
- .credits_pending = math.maxInt(u64),
1089
- .credits_posted = math.maxInt(u64),
1187
+ .debits_pending = 1,
1188
+ .debits_posted = 1,
1189
+ .credits_pending = 1,
1190
+ .credits_posted = 1,
1090
1191
  .timestamp = 2,
1091
1192
  }),
1092
1193
  },
@@ -1102,10 +1203,10 @@ test "create/lookup/rollback accounts" {
1102
1203
  .debits_must_not_exceed_credits = true,
1103
1204
  .credits_must_not_exceed_debits = true,
1104
1205
  },
1105
- .debits_pending = math.maxInt(u64),
1106
- .debits_posted = math.maxInt(u64),
1107
- .credits_pending = math.maxInt(u64),
1108
- .credits_posted = math.maxInt(u64),
1206
+ .debits_pending = 1,
1207
+ .debits_posted = 1,
1208
+ .credits_pending = 1,
1209
+ .credits_posted = 1,
1109
1210
  .timestamp = 2,
1110
1211
  }),
1111
1212
  },
@@ -1121,10 +1222,10 @@ test "create/lookup/rollback accounts" {
1121
1222
  .debits_must_not_exceed_credits = true,
1122
1223
  .credits_must_not_exceed_debits = true,
1123
1224
  },
1124
- .debits_pending = math.maxInt(u64),
1125
- .debits_posted = math.maxInt(u64),
1126
- .credits_pending = math.maxInt(u64),
1127
- .credits_posted = math.maxInt(u64),
1225
+ .debits_pending = 1,
1226
+ .debits_posted = 1,
1227
+ .credits_pending = 1,
1228
+ .credits_posted = 1,
1128
1229
  .timestamp = 2,
1129
1230
  }),
1130
1231
  },
@@ -1139,10 +1240,10 @@ test "create/lookup/rollback accounts" {
1139
1240
  .debits_must_not_exceed_credits = true,
1140
1241
  .credits_must_not_exceed_debits = true,
1141
1242
  },
1142
- .debits_pending = math.maxInt(u64),
1143
- .debits_posted = math.maxInt(u64),
1144
- .credits_pending = math.maxInt(u64),
1145
- .credits_posted = math.maxInt(u64),
1243
+ .debits_pending = 1,
1244
+ .debits_posted = 1,
1245
+ .credits_pending = 1,
1246
+ .credits_posted = 1,
1146
1247
  .timestamp = 2,
1147
1248
  }),
1148
1249
  },
@@ -1165,7 +1266,7 @@ test "create/lookup/rollback accounts" {
1165
1266
  }),
1166
1267
  },
1167
1268
  .{
1168
- .result = .overflows_debits,
1269
+ .result = .debits_pending_must_be_zero,
1169
1270
  .object = mem.zeroInit(Account, .{
1170
1271
  .id = 1,
1171
1272
  .user_data = 20,
@@ -1174,32 +1275,31 @@ test "create/lookup/rollback accounts" {
1174
1275
  .flags = .{
1175
1276
  .debits_must_not_exceed_credits = true,
1176
1277
  },
1177
- .debits_pending = math.maxInt(u64),
1178
- .debits_posted = 60,
1179
- .credits_pending = math.maxInt(u64),
1180
- .credits_posted = 80,
1278
+ .debits_pending = 1,
1279
+ .debits_posted = 1,
1280
+ .credits_pending = 1,
1281
+ .credits_posted = 1,
1181
1282
  .timestamp = 2,
1182
1283
  }),
1183
1284
  },
1184
1285
  .{
1185
- .result = .overflows_credits,
1286
+ .result = .debits_posted_must_be_zero,
1186
1287
  .object = mem.zeroInit(Account, .{
1187
1288
  .id = 1,
1188
1289
  .user_data = 20,
1189
1290
  .ledger = 30,
1190
1291
  .code = 40,
1191
1292
  .flags = .{
1192
- .credits_must_not_exceed_debits = true,
1293
+ .debits_must_not_exceed_credits = true,
1193
1294
  },
1194
- .debits_pending = 50,
1195
- .debits_posted = 60,
1196
- .credits_pending = math.maxInt(u64),
1197
- .credits_posted = 80,
1295
+ .debits_posted = 1,
1296
+ .credits_pending = 1,
1297
+ .credits_posted = 1,
1198
1298
  .timestamp = 2,
1199
1299
  }),
1200
1300
  },
1201
1301
  .{
1202
- .result = .exceeds_credits,
1302
+ .result = .credits_pending_must_be_zero,
1203
1303
  .object = mem.zeroInit(Account, .{
1204
1304
  .id = 1,
1205
1305
  .user_data = 20,
@@ -1208,27 +1308,22 @@ test "create/lookup/rollback accounts" {
1208
1308
  .flags = .{
1209
1309
  .debits_must_not_exceed_credits = true,
1210
1310
  },
1211
- .debits_pending = 50,
1212
- .debits_posted = 60,
1213
1311
  .credits_pending = 1,
1214
- .credits_posted = 109,
1312
+ .credits_posted = 1,
1215
1313
  .timestamp = 2,
1216
1314
  }),
1217
1315
  },
1218
1316
  .{
1219
- .result = .exceeds_debits,
1317
+ .result = .credits_posted_must_be_zero,
1220
1318
  .object = mem.zeroInit(Account, .{
1221
1319
  .id = 1,
1222
1320
  .user_data = 20,
1223
1321
  .ledger = 30,
1224
1322
  .code = 40,
1225
1323
  .flags = .{
1226
- .credits_must_not_exceed_debits = true,
1324
+ .debits_must_not_exceed_credits = true,
1227
1325
  },
1228
- .debits_pending = 50,
1229
- .debits_posted = 60,
1230
- .credits_pending = 1,
1231
- .credits_posted = 109,
1326
+ .credits_posted = 1,
1232
1327
  .timestamp = 2,
1233
1328
  }),
1234
1329
  },
@@ -1242,10 +1337,6 @@ test "create/lookup/rollback accounts" {
1242
1337
  .flags = .{
1243
1338
  .credits_must_not_exceed_debits = true,
1244
1339
  },
1245
- .debits_pending = 50,
1246
- .debits_posted = 60,
1247
- .credits_pending = 0,
1248
- .credits_posted = 0,
1249
1340
  .timestamp = 2,
1250
1341
  }),
1251
1342
  },
@@ -1256,10 +1347,6 @@ test "create/lookup/rollback accounts" {
1256
1347
  .user_data = 20,
1257
1348
  .ledger = 30,
1258
1349
  .code = 40,
1259
- .debits_pending = 50,
1260
- .debits_posted = 60,
1261
- .credits_pending = 70,
1262
- .credits_posted = 80,
1263
1350
  .timestamp = 2,
1264
1351
  }),
1265
1352
  },
@@ -1270,10 +1357,6 @@ test "create/lookup/rollback accounts" {
1270
1357
  .user_data = 2,
1271
1358
  .ledger = 30,
1272
1359
  .code = 40,
1273
- .debits_pending = 50,
1274
- .debits_posted = 60,
1275
- .credits_pending = 70,
1276
- .credits_posted = 80,
1277
1360
  .timestamp = 2,
1278
1361
  }),
1279
1362
  },
@@ -1284,66 +1367,6 @@ test "create/lookup/rollback accounts" {
1284
1367
  .user_data = 2,
1285
1368
  .ledger = 3,
1286
1369
  .code = 40,
1287
- .debits_pending = 50,
1288
- .debits_posted = 60,
1289
- .credits_pending = 70,
1290
- .credits_posted = 80,
1291
- .timestamp = 2,
1292
- }),
1293
- },
1294
- .{
1295
- .result = .exists_with_different_debits_pending,
1296
- .object = mem.zeroInit(Account, .{
1297
- .id = 1,
1298
- .user_data = 2,
1299
- .ledger = 3,
1300
- .code = 4,
1301
- .debits_pending = 50,
1302
- .debits_posted = 60,
1303
- .credits_pending = 70,
1304
- .credits_posted = 80,
1305
- .timestamp = 2,
1306
- }),
1307
- },
1308
- .{
1309
- .result = .exists_with_different_debits_posted,
1310
- .object = mem.zeroInit(Account, .{
1311
- .id = 1,
1312
- .user_data = 2,
1313
- .ledger = 3,
1314
- .code = 4,
1315
- .debits_pending = 5,
1316
- .debits_posted = 60,
1317
- .credits_pending = 70,
1318
- .credits_posted = 80,
1319
- .timestamp = 2,
1320
- }),
1321
- },
1322
- .{
1323
- .result = .exists_with_different_credits_pending,
1324
- .object = mem.zeroInit(Account, .{
1325
- .id = 1,
1326
- .user_data = 2,
1327
- .ledger = 3,
1328
- .code = 4,
1329
- .debits_pending = 5,
1330
- .debits_posted = 6,
1331
- .credits_pending = 70,
1332
- .credits_posted = 80,
1333
- .timestamp = 2,
1334
- }),
1335
- },
1336
- .{
1337
- .result = .exists_with_different_credits_posted,
1338
- .object = mem.zeroInit(Account, .{
1339
- .id = 1,
1340
- .user_data = 2,
1341
- .ledger = 3,
1342
- .code = 4,
1343
- .debits_pending = 5,
1344
- .debits_posted = 6,
1345
- .credits_pending = 7,
1346
- .credits_posted = 80,
1347
1370
  .timestamp = 2,
1348
1371
  }),
1349
1372
  },
@@ -1354,22 +1377,13 @@ test "create/lookup/rollback accounts" {
1354
1377
  .user_data = 2,
1355
1378
  .ledger = 3,
1356
1379
  .code = 4,
1357
- .debits_pending = 5,
1358
- .debits_posted = 6,
1359
- .credits_pending = 7,
1360
- .credits_posted = 8,
1361
1380
  .timestamp = 2,
1362
1381
  }),
1363
1382
  },
1364
1383
  };
1365
1384
 
1366
1385
  var context: TestContext = undefined;
1367
- try context.init(testing.allocator, .{
1368
- .lsm_forest_node_count = 1,
1369
- .cache_size_accounts = vectors.len,
1370
- .cache_size_transfers = 0,
1371
- .cache_size_posted = 0,
1372
- });
1386
+ try context.init(testing.allocator);
1373
1387
  defer context.deinit(testing.allocator);
1374
1388
 
1375
1389
  const state_machine = &context.state_machine;
@@ -1391,10 +1405,6 @@ test "create/lookup/rollback accounts" {
1391
1405
  }
1392
1406
 
1393
1407
  test "linked accounts" {
1394
- const accounts_max = 5;
1395
- const transfers_max = 0;
1396
- const transfers_pending_max = 0;
1397
-
1398
1408
  var accounts = [_]Account{
1399
1409
  // An individual event (successful):
1400
1410
  mem.zeroInit(Account, .{ .id = 7, .code = 1, .ledger = 1 }),
@@ -1430,23 +1440,18 @@ test "linked accounts" {
1430
1440
  };
1431
1441
 
1432
1442
  var context: TestContext = undefined;
1433
- try context.init(testing.allocator, .{
1434
- .lsm_forest_node_count = 1,
1435
- .cache_size_accounts = accounts_max,
1436
- .cache_size_transfers = transfers_max,
1437
- .cache_size_posted = transfers_pending_max,
1438
- });
1443
+ try context.init(testing.allocator);
1439
1444
  defer context.deinit(testing.allocator);
1440
1445
 
1441
1446
  const state_machine = &context.state_machine;
1442
1447
 
1443
1448
  const input = mem.asBytes(&accounts);
1444
1449
 
1445
- const output = try testing.allocator.alloc(u8, 4096);
1450
+ const output = try testing.allocator.alignedAlloc(u8, 16, 4096);
1446
1451
  defer testing.allocator.free(output);
1447
1452
 
1448
1453
  _ = state_machine.prepare(.create_accounts, input);
1449
- const size = state_machine.commit(0, 0, .create_accounts, input, output);
1454
+ const size = state_machine.commit(0, 1, .create_accounts, input, output);
1450
1455
  const results = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1451
1456
 
1452
1457
  try expectEqualSlices(
@@ -1474,6 +1479,124 @@ test "linked accounts" {
1474
1479
  // All our rollback handlers appear to be commutative.
1475
1480
  }
1476
1481
 
1482
+ test "linked_event_chain_open" {
1483
+ var accounts = [_]Account{
1484
+ // A chain of 3 events (the last event in the chain closes the chain with linked=false):
1485
+ mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1486
+ mem.zeroInit(Account, .{ .id = 2, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1487
+ mem.zeroInit(Account, .{ .id = 3, .code = 1, .ledger = 1 }),
1488
+
1489
+ // An open chain of 2 events:
1490
+ mem.zeroInit(Account, .{ .id = 4, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1491
+ mem.zeroInit(Account, .{ .id = 5, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1492
+ };
1493
+
1494
+ var context: TestContext = undefined;
1495
+ try context.init(testing.allocator);
1496
+ defer context.deinit(testing.allocator);
1497
+
1498
+ const state_machine = &context.state_machine;
1499
+
1500
+ const input = mem.asBytes(&accounts);
1501
+
1502
+ const output = try testing.allocator.alignedAlloc(u8, 16, 4096);
1503
+ defer testing.allocator.free(output);
1504
+
1505
+ _ = state_machine.prepare(.create_accounts, input);
1506
+ const size = state_machine.commit(0, 1, .create_accounts, input, output);
1507
+ const results = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1508
+
1509
+ try expectEqualSlices(
1510
+ CreateAccountsResult,
1511
+ &[_]CreateAccountsResult{
1512
+ .{ .index = 3, .result = .linked_event_failed },
1513
+ .{ .index = 4, .result = .linked_event_chain_open },
1514
+ },
1515
+ results,
1516
+ );
1517
+
1518
+ try expectEqual(accounts[0], state_machine.get_account(accounts[0].id).?.*);
1519
+ try expectEqual(accounts[1], state_machine.get_account(accounts[1].id).?.*);
1520
+ try expectEqual(accounts[2], state_machine.get_account(accounts[2].id).?.*);
1521
+
1522
+ try expectEqual(@as(?*const Account, null), state_machine.get_account(accounts[3].id));
1523
+ try expectEqual(@as(?*const Account, null), state_machine.get_account(accounts[4].id));
1524
+ }
1525
+
1526
+ test "linked_event_chain_open for an already failed batch" {
1527
+ var accounts = [_]Account{
1528
+ // An individual event (successful):
1529
+ mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1 }),
1530
+
1531
+ // An open chain of 3 events (the second one fails):
1532
+ mem.zeroInit(Account, .{ .id = 2, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1533
+ mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1534
+ mem.zeroInit(Account, .{ .id = 3, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1535
+ };
1536
+
1537
+ var context: TestContext = undefined;
1538
+ try context.init(testing.allocator);
1539
+ defer context.deinit(testing.allocator);
1540
+
1541
+ const state_machine = &context.state_machine;
1542
+
1543
+ const input = mem.asBytes(&accounts);
1544
+
1545
+ const output = try testing.allocator.alignedAlloc(u8, 16, 4096);
1546
+ defer testing.allocator.free(output);
1547
+
1548
+ _ = state_machine.prepare(.create_accounts, input);
1549
+ const size = state_machine.commit(0, 1, .create_accounts, input, output);
1550
+ const results = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1551
+
1552
+ try expectEqualSlices(
1553
+ CreateAccountsResult,
1554
+ &[_]CreateAccountsResult{
1555
+ .{ .index = 1, .result = .linked_event_failed },
1556
+ .{ .index = 2, .result = .exists_with_different_flags },
1557
+ .{ .index = 3, .result = .linked_event_chain_open },
1558
+ },
1559
+ results,
1560
+ );
1561
+
1562
+ try expectEqual(accounts[0], state_machine.get_account(accounts[0].id).?.*);
1563
+
1564
+ try expectEqual(@as(?*const Account, null), state_machine.get_account(accounts[1].id));
1565
+ try expectEqual(@as(?*const Account, null), state_machine.get_account(accounts[3].id));
1566
+ }
1567
+
1568
+ test "linked_event_chain_open for a batch of 1" {
1569
+ var accounts = [_]Account{
1570
+ // Just one event with linked = true
1571
+ mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
1572
+ };
1573
+
1574
+ var context: TestContext = undefined;
1575
+ try context.init(testing.allocator);
1576
+ defer context.deinit(testing.allocator);
1577
+
1578
+ const state_machine = &context.state_machine;
1579
+
1580
+ const input = mem.asBytes(&accounts);
1581
+
1582
+ const output = try testing.allocator.alignedAlloc(u8, 16, 4096);
1583
+ defer testing.allocator.free(output);
1584
+
1585
+ _ = state_machine.prepare(.create_accounts, input);
1586
+ const size = state_machine.commit(0, 1, .create_accounts, input, output);
1587
+ const results = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1588
+
1589
+ try expectEqualSlices(
1590
+ CreateAccountsResult,
1591
+ &[_]CreateAccountsResult{
1592
+ .{ .index = 0, .result = .linked_event_chain_open },
1593
+ },
1594
+ results,
1595
+ );
1596
+
1597
+ try expectEqual(@as(?*const Account, null), state_machine.get_account(accounts[0].id));
1598
+ }
1599
+
1477
1600
  // The goal is to ensure that:
1478
1601
  // 1. all CreateTransferResult enums are covered, with
1479
1602
  // 2. enums tested in the order that they are defined, for easier auditing of coverage, and that
@@ -1518,34 +1641,26 @@ test "create/lookup/rollback transfers" {
1518
1641
  };
1519
1642
 
1520
1643
  var context: TestContext = undefined;
1521
- try context.init(testing.allocator, .{
1522
- .lsm_forest_node_count = 1,
1523
- .cache_size_accounts = accounts.len,
1524
- .cache_size_transfers = 1,
1525
- .cache_size_posted = 0,
1526
- });
1644
+ try context.init(testing.allocator);
1527
1645
  defer context.deinit(testing.allocator);
1528
1646
 
1529
1647
  const state_machine = &context.state_machine;
1530
1648
 
1531
1649
  const input = mem.asBytes(&accounts);
1532
1650
 
1533
- const output = try testing.allocator.alloc(u8, 4096);
1651
+ const output = try testing.allocator.alignedAlloc(u8, 16, 4096);
1534
1652
  defer testing.allocator.free(output);
1535
1653
 
1536
1654
  _ = state_machine.prepare(.create_accounts, input);
1537
- const size = state_machine.commit(0, 0, .create_accounts, input, output);
1538
-
1539
- const errors = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
1540
- try expect(errors.len == 0);
1541
1655
 
1542
1656
  for (accounts) |account| {
1657
+ state_machine.forest.grooves.accounts.put(&account);
1543
1658
  try expectEqual(account, state_machine.get_account(account.id).?.*);
1544
1659
  }
1545
1660
 
1546
1661
  const Vector = struct { result: CreateTransferResult, object: Transfer };
1547
1662
 
1548
- const timestamp: u64 = (state_machine.commit_timestamp + 1);
1663
+ const timestamp: u64 = state_machine.prepare_timestamp + 1;
1549
1664
  const vectors = [_]Vector{
1550
1665
  .{
1551
1666
  .result = .reserved_flag,
@@ -1719,7 +1834,7 @@ test "create/lookup/rollback transfers" {
1719
1834
  .id = 1,
1720
1835
  .debit_account_id = 100,
1721
1836
  .credit_account_id = 200,
1722
- .timeout = 1,
1837
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1723
1838
  .ledger = 0,
1724
1839
  .code = 0,
1725
1840
  .amount = 0,
@@ -1732,7 +1847,7 @@ test "create/lookup/rollback transfers" {
1732
1847
  .id = 1,
1733
1848
  .debit_account_id = 100,
1734
1849
  .credit_account_id = 200,
1735
- .timeout = 1,
1850
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1736
1851
  .ledger = 0,
1737
1852
  .code = 0,
1738
1853
  .flags = .{ .pending = true },
@@ -1746,7 +1861,7 @@ test "create/lookup/rollback transfers" {
1746
1861
  .id = 1,
1747
1862
  .debit_account_id = 100,
1748
1863
  .credit_account_id = 200,
1749
- .timeout = 1,
1864
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1750
1865
  .ledger = 100,
1751
1866
  .code = 0,
1752
1867
  .flags = .{ .pending = true },
@@ -1760,7 +1875,7 @@ test "create/lookup/rollback transfers" {
1760
1875
  .id = 1,
1761
1876
  .debit_account_id = 100,
1762
1877
  .credit_account_id = 200,
1763
- .timeout = 1,
1878
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1764
1879
  .ledger = 100,
1765
1880
  .code = 1,
1766
1881
  .flags = .{ .pending = true },
@@ -1774,7 +1889,7 @@ test "create/lookup/rollback transfers" {
1774
1889
  .id = 1,
1775
1890
  .debit_account_id = 100,
1776
1891
  .credit_account_id = 200,
1777
- .timeout = 1,
1892
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1778
1893
  .ledger = 100,
1779
1894
  .code = 1,
1780
1895
  .flags = .{ .pending = true },
@@ -1788,7 +1903,7 @@ test "create/lookup/rollback transfers" {
1788
1903
  .id = 1,
1789
1904
  .debit_account_id = 1,
1790
1905
  .credit_account_id = 200,
1791
- .timeout = 1,
1906
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1792
1907
  .ledger = 100,
1793
1908
  .code = 1,
1794
1909
  .flags = .{ .pending = true },
@@ -1802,9 +1917,11 @@ test "create/lookup/rollback transfers" {
1802
1917
  .id = 1,
1803
1918
  .debit_account_id = 1,
1804
1919
  .credit_account_id = 2,
1920
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1805
1921
  .ledger = 100,
1806
1922
  .code = 1,
1807
1923
  .amount = 1,
1924
+ .flags = .{ .pending = true },
1808
1925
  .timestamp = timestamp,
1809
1926
  }),
1810
1927
  },
@@ -1814,9 +1931,11 @@ test "create/lookup/rollback transfers" {
1814
1931
  .id = 1,
1815
1932
  .debit_account_id = 1,
1816
1933
  .credit_account_id = 3,
1934
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1817
1935
  .ledger = 100,
1818
1936
  .code = 1,
1819
1937
  .amount = 1,
1938
+ .flags = .{ .pending = true },
1820
1939
  .timestamp = timestamp,
1821
1940
  }),
1822
1941
  },
@@ -1826,7 +1945,7 @@ test "create/lookup/rollback transfers" {
1826
1945
  .id = 1,
1827
1946
  .debit_account_id = 1,
1828
1947
  .credit_account_id = 3,
1829
- .timeout = 30000,
1948
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1830
1949
  .ledger = 1,
1831
1950
  .code = 1,
1832
1951
  .flags = .{ .pending = true },
@@ -1840,7 +1959,7 @@ test "create/lookup/rollback transfers" {
1840
1959
  .id = 1,
1841
1960
  .debit_account_id = 1,
1842
1961
  .credit_account_id = 3,
1843
- .timeout = 30000,
1962
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1844
1963
  .ledger = 1,
1845
1964
  .code = 1,
1846
1965
  .flags = .{ .pending = true },
@@ -1854,8 +1973,10 @@ test "create/lookup/rollback transfers" {
1854
1973
  .id = 1,
1855
1974
  .debit_account_id = 1,
1856
1975
  .credit_account_id = 3,
1976
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1857
1977
  .ledger = 1,
1858
1978
  .code = 1,
1979
+ .flags = .{ .pending = true },
1859
1980
  .amount = math.maxInt(u64) - accounts[1 - 1].debits_posted + 1,
1860
1981
  .timestamp = timestamp,
1861
1982
  }),
@@ -1866,8 +1987,10 @@ test "create/lookup/rollback transfers" {
1866
1987
  .id = 1,
1867
1988
  .debit_account_id = 1,
1868
1989
  .credit_account_id = 3,
1990
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1869
1991
  .ledger = 1,
1870
1992
  .code = 1,
1993
+ .flags = .{ .pending = true },
1871
1994
  .amount = math.maxInt(u64) - accounts[3 - 1].credits_posted + 1,
1872
1995
  .timestamp = timestamp,
1873
1996
  }),
@@ -1878,8 +2001,10 @@ test "create/lookup/rollback transfers" {
1878
2001
  .id = 1,
1879
2002
  .debit_account_id = 1,
1880
2003
  .credit_account_id = 3,
2004
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1881
2005
  .ledger = 1,
1882
2006
  .code = 1,
2007
+ .flags = .{ .pending = true },
1883
2008
  .amount = math.maxInt(u64) -
1884
2009
  accounts[1 - 1].debits_pending -
1885
2010
  accounts[1 - 1].debits_posted + 1,
@@ -1892,14 +2017,32 @@ test "create/lookup/rollback transfers" {
1892
2017
  .id = 1,
1893
2018
  .debit_account_id = 1,
1894
2019
  .credit_account_id = 3,
2020
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
1895
2021
  .ledger = 1,
1896
2022
  .code = 1,
2023
+ .flags = .{ .pending = true },
1897
2024
  .amount = math.maxInt(u64) -
1898
2025
  accounts[3 - 1].credits_pending -
1899
2026
  accounts[3 - 1].credits_posted + 1,
1900
2027
  .timestamp = timestamp,
1901
2028
  }),
1902
2029
  },
2030
+ .{
2031
+ .result = .overflows_timeout,
2032
+ .object = mem.zeroInit(Transfer, .{
2033
+ .id = 1,
2034
+ .debit_account_id = 4,
2035
+ .credit_account_id = 5,
2036
+ .timeout = (std.math.maxInt(u64) - timestamp) + 1,
2037
+ .ledger = 1,
2038
+ .code = 1,
2039
+ .flags = .{ .pending = true },
2040
+ .amount = accounts[4 - 1].credits_posted -
2041
+ accounts[4 - 1].debits_pending -
2042
+ accounts[4 - 1].debits_posted + 1,
2043
+ .timestamp = timestamp,
2044
+ }),
2045
+ },
1903
2046
  .{
1904
2047
  .result = .exceeds_credits,
1905
2048
  .object = mem.zeroInit(Transfer, .{
@@ -2132,11 +2275,6 @@ test "create/lookup/rollback transfers" {
2132
2275
  try test_account_balances(state_machine, 1, 100, 200, 0, 0);
2133
2276
  try test_account_balances(state_machine, 3, 0, 0, 110, 210);
2134
2277
  try expect(state_machine.get_transfer(1) == null);
2135
-
2136
- for (accounts) |account| {
2137
- state_machine.create_account_rollback(&account);
2138
- try expect(state_machine.get_account(account.id) == null);
2139
- }
2140
2278
  }
2141
2279
 
2142
2280
  test "create/lookup/rollback 2-phase transfers" {
@@ -2198,12 +2336,7 @@ test "create/lookup/rollback 2-phase transfers" {
2198
2336
  };
2199
2337
 
2200
2338
  var context: TestContext = undefined;
2201
- try context.init(testing.allocator, .{
2202
- .lsm_forest_node_count = 1,
2203
- .cache_size_accounts = accounts.len,
2204
- .cache_size_transfers = 100,
2205
- .cache_size_posted = 1,
2206
- });
2339
+ try context.init(testing.allocator);
2207
2340
  defer context.deinit(testing.allocator);
2208
2341
 
2209
2342
  const state_machine = &context.state_machine;
@@ -2211,12 +2344,12 @@ test "create/lookup/rollback 2-phase transfers" {
2211
2344
  // Create accounts:
2212
2345
  const accounts_input = mem.asBytes(&accounts);
2213
2346
 
2214
- const accounts_output = try testing.allocator.alloc(u8, 4096);
2347
+ const accounts_output = try testing.allocator.alignedAlloc(u8, 16, 4096);
2215
2348
  defer testing.allocator.free(accounts_output);
2216
2349
 
2217
2350
  const accounts_timestamp = state_machine.prepare(.create_accounts, accounts_input);
2218
2351
  {
2219
- const size = state_machine.commit(0, 0, .create_accounts, accounts_input, accounts_output);
2352
+ const size = state_machine.commit(0, 1, .create_accounts, accounts_input, accounts_output);
2220
2353
  const errors = mem.bytesAsSlice(CreateAccountsResult, accounts_output[0..size]);
2221
2354
  try expectEqual(@as(usize, 0), errors.len);
2222
2355
  }
@@ -2227,13 +2360,13 @@ test "create/lookup/rollback 2-phase transfers" {
2227
2360
  // Create pending transfers:
2228
2361
  const transfers_input = mem.asBytes(&transfers);
2229
2362
 
2230
- const transfers_output = try testing.allocator.alloc(u8, 4096);
2363
+ const transfers_output = try testing.allocator.alignedAlloc(u8, 16, 4096);
2231
2364
  defer testing.allocator.free(transfers_output);
2232
2365
 
2233
2366
  const transfers_timestamp = state_machine.prepare(.create_transfers, transfers_input);
2234
2367
  try testing.expect(transfers_timestamp > accounts_timestamp);
2235
2368
  {
2236
- const size = state_machine.commit(0, 1, .create_transfers, transfers_input, transfers_output);
2369
+ const size = state_machine.commit(0, 2, .create_transfers, transfers_input, transfers_output);
2237
2370
  const errors = mem.bytesAsSlice(CreateTransfersResult, transfers_output[0..size]);
2238
2371
  try expectEqual(@as(usize, 0), errors.len);
2239
2372
  }
@@ -2901,7 +3034,9 @@ fn test_equal_n_bytes(comptime n: usize) !void {
2901
3034
 
2902
3035
  test "StateMachine: ref all decls" {
2903
3036
  const Storage = @import("storage.zig").Storage;
2904
- const StateMachine = StateMachineType(Storage);
3037
+ const StateMachine = StateMachineType(Storage, .{
3038
+ .message_body_size_max = 1000 * @sizeOf(Account),
3039
+ });
2905
3040
 
2906
3041
  std.testing.refAllDecls(StateMachine);
2907
3042
  }