tigerbeetle-node 0.11.11 → 0.11.13

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 (64) hide show
  1. package/README.md +212 -196
  2. package/dist/.client.node.sha256 +1 -1
  3. package/package.json +6 -8
  4. package/scripts/build_lib.sh +22 -2
  5. package/src/node.zig +1 -0
  6. package/src/tigerbeetle/scripts/benchmark.bat +9 -5
  7. package/src/tigerbeetle/scripts/benchmark.sh +1 -4
  8. package/src/tigerbeetle/scripts/fail_on_diff.sh +9 -0
  9. package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +12 -0
  10. package/src/tigerbeetle/scripts/scripts/benchmark.bat +55 -0
  11. package/src/tigerbeetle/scripts/scripts/benchmark.sh +66 -0
  12. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +44 -0
  13. package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +9 -0
  14. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +15 -0
  15. package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +12 -0
  16. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +7 -0
  17. package/src/tigerbeetle/scripts/scripts/install.bat +7 -0
  18. package/src/tigerbeetle/scripts/scripts/install.sh +21 -0
  19. package/src/tigerbeetle/scripts/scripts/install_zig.bat +113 -0
  20. package/src/tigerbeetle/scripts/scripts/install_zig.sh +90 -0
  21. package/src/tigerbeetle/scripts/scripts/lint.zig +199 -0
  22. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +9 -0
  23. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +5 -0
  24. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +10 -0
  25. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +14 -0
  26. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +48 -0
  27. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +23 -0
  28. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +46 -0
  29. package/src/tigerbeetle/src/benchmark.zig +253 -231
  30. package/src/tigerbeetle/src/config.zig +2 -3
  31. package/src/tigerbeetle/src/constants.zig +2 -10
  32. package/src/tigerbeetle/src/io/linux.zig +15 -6
  33. package/src/tigerbeetle/src/lsm/forest.zig +1 -0
  34. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +63 -14
  35. package/src/tigerbeetle/src/lsm/groove.zig +134 -70
  36. package/src/tigerbeetle/src/lsm/level_iterator.zig +2 -2
  37. package/src/tigerbeetle/src/lsm/manifest_level.zig +1 -0
  38. package/src/tigerbeetle/src/lsm/posted_groove.zig +7 -4
  39. package/src/tigerbeetle/src/lsm/segmented_array.zig +1 -0
  40. package/src/tigerbeetle/src/lsm/table.zig +29 -51
  41. package/src/tigerbeetle/src/lsm/table_immutable.zig +6 -17
  42. package/src/tigerbeetle/src/lsm/table_iterator.zig +2 -2
  43. package/src/tigerbeetle/src/lsm/table_mutable.zig +9 -26
  44. package/src/tigerbeetle/src/lsm/test.zig +1 -0
  45. package/src/tigerbeetle/src/lsm/tree.zig +2 -26
  46. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +7 -2
  47. package/src/tigerbeetle/src/message_bus.zig +2 -1
  48. package/src/tigerbeetle/src/simulator.zig +14 -3
  49. package/src/tigerbeetle/src/state_machine/auditor.zig +1 -0
  50. package/src/tigerbeetle/src/state_machine.zig +402 -184
  51. package/src/tigerbeetle/src/stdx.zig +32 -0
  52. package/src/tigerbeetle/src/testing/cluster/network.zig +6 -7
  53. package/src/tigerbeetle/src/testing/cluster.zig +6 -5
  54. package/src/tigerbeetle/src/testing/packet_simulator.zig +19 -10
  55. package/src/tigerbeetle/src/testing/state_machine.zig +1 -0
  56. package/src/tigerbeetle/src/unit_tests.zig +20 -22
  57. package/src/tigerbeetle/src/vsr/README.md +209 -0
  58. package/src/tigerbeetle/src/vsr/client.zig +4 -4
  59. package/src/tigerbeetle/src/vsr/clock.zig +2 -0
  60. package/src/tigerbeetle/src/vsr/journal.zig +2 -0
  61. package/src/tigerbeetle/src/vsr/replica.zig +646 -578
  62. package/src/tigerbeetle/src/vsr/superblock.zig +14 -17
  63. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +5 -5
  64. package/src/tigerbeetle/src/vsr.zig +370 -37
@@ -24,6 +24,7 @@ const CreateTransferResult = tb.CreateTransferResult;
24
24
 
25
25
  pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
26
26
  message_body_size_max: usize,
27
+ lsm_batch_multiple: usize,
27
28
  }) type {
28
29
  return struct {
29
30
  const StateMachine = @This();
@@ -31,28 +32,190 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
31
32
  const GrooveType = @import("lsm/groove.zig").GrooveType;
32
33
  const ForestType = @import("lsm/forest.zig").ForestType;
33
34
 
34
- const AccountsGroove = GrooveType(
35
+ pub const constants = struct {
36
+ pub const message_body_size_max = constants_.message_body_size_max;
37
+
38
+ /// The maximum number of objects within a batch, by operation.
39
+ pub const batch_max = struct {
40
+ pub const create_accounts = operation_batch_max(.create_accounts);
41
+ pub const create_transfers = operation_batch_max(.create_transfers);
42
+ pub const lookup_accounts = operation_batch_max(.lookup_accounts);
43
+ pub const lookup_transfers = operation_batch_max(.lookup_transfers);
44
+
45
+ comptime {
46
+ assert(create_accounts > 0);
47
+ assert(create_transfers > 0);
48
+ assert(lookup_accounts > 0);
49
+ assert(lookup_transfers > 0);
50
+ }
51
+
52
+ fn operation_batch_max(comptime operation: Operation) usize {
53
+ return @divFloor(message_body_size_max, std.math.max(
54
+ @sizeOf(Event(operation)),
55
+ @sizeOf(Result(operation)),
56
+ ));
57
+ }
58
+ };
59
+ };
60
+
61
+ pub const AccountImmutable = extern struct {
62
+ id: u128,
63
+ user_data: u128,
64
+ timestamp: u64,
65
+ ledger: u32,
66
+ code: u16,
67
+ flags: AccountFlags,
68
+ padding: [16]u8,
69
+
70
+ comptime {
71
+ assert(@sizeOf(AccountImmutable) == 64);
72
+ assert(@bitSizeOf(AccountImmutable) == @sizeOf(AccountImmutable) * 8);
73
+ }
74
+
75
+ pub fn from_account(a: *const Account) AccountImmutable {
76
+ return .{
77
+ .id = a.id,
78
+ .user_data = a.user_data,
79
+ .timestamp = a.timestamp,
80
+ .ledger = a.ledger,
81
+ .code = a.code,
82
+ .flags = a.flags,
83
+ .padding = mem.zeroes([16]u8),
84
+ };
85
+ }
86
+ };
87
+
88
+ pub const AccountMutable = extern struct {
89
+ debits_pending: u64,
90
+ debits_posted: u64,
91
+ credits_pending: u64,
92
+ credits_posted: u64,
93
+ timestamp: u64,
94
+ padding: [24]u8,
95
+
96
+ comptime {
97
+ assert(@sizeOf(AccountMutable) == 64);
98
+ assert(@bitSizeOf(AccountMutable) == @sizeOf(AccountMutable) * 8);
99
+ }
100
+
101
+ pub fn from_account(a: *const Account) AccountMutable {
102
+ return .{
103
+ .debits_pending = a.debits_pending,
104
+ .debits_posted = a.debits_posted,
105
+ .credits_pending = a.credits_pending,
106
+ .credits_posted = a.credits_posted,
107
+ .timestamp = a.timestamp,
108
+ .padding = mem.zeroes([24]u8),
109
+ };
110
+ }
111
+ };
112
+
113
+ pub fn into_account(immut: *const AccountImmutable, mut: *const AccountMutable) Account {
114
+ assert(immut.timestamp == mut.timestamp);
115
+ return Account{
116
+ .id = immut.id,
117
+ .user_data = immut.user_data,
118
+ .reserved = mem.zeroes([48]u8),
119
+ .ledger = immut.ledger,
120
+ .code = immut.code,
121
+ .flags = immut.flags,
122
+ .debits_pending = mut.debits_pending,
123
+ .debits_posted = mut.debits_posted,
124
+ .credits_pending = mut.credits_pending,
125
+ .credits_posted = mut.credits_posted,
126
+ .timestamp = mut.timestamp,
127
+ };
128
+ }
129
+
130
+ const AccountsImmutableGroove = GrooveType(
35
131
  Storage,
36
- Account,
132
+ AccountImmutable,
37
133
  .{
38
- .ignored = &[_][]const u8{ "reserved", "flags" },
134
+ .value_count_max = .{
135
+ .timestamp = constants_.lsm_batch_multiple * math.max(
136
+ constants.batch_max.create_accounts,
137
+ // ×2 because creating a transfer will update 2 accounts.
138
+ 2 * constants.batch_max.create_transfers,
139
+ ),
140
+ .id = constants_.lsm_batch_multiple * constants.batch_max.create_accounts,
141
+ .user_data = constants_.lsm_batch_multiple * constants.batch_max.create_accounts,
142
+ .ledger = constants_.lsm_batch_multiple * constants.batch_max.create_accounts,
143
+ .code = constants_.lsm_batch_multiple * constants.batch_max.create_accounts,
144
+ },
145
+ .ignored = &[_][]const u8{ "flags", "padding" },
39
146
  .derived = .{},
40
147
  },
41
148
  );
149
+
150
+ const AccountsMutableGroove = GrooveType(
151
+ Storage,
152
+ AccountMutable,
153
+ .{
154
+ .value_count_max = .{
155
+ .timestamp = constants_.lsm_batch_multiple * math.max(
156
+ constants.batch_max.create_accounts,
157
+ // ×2 because creating a transfer will update 2 accounts.
158
+ 2 * constants.batch_max.create_transfers,
159
+ ),
160
+ // Transfers mutate the secondary indices for debits/credits pending/posted.
161
+ //
162
+ // * Each mutation results in a remove and an insert: the ×2 multiplier.
163
+ // * Each transfer modifies two accounts. However, this does not
164
+ // necessitate an additional ×2 multiplier — the credits of the debit
165
+ // account and the debits of the credit account are not modified.
166
+ .debits_pending = constants_.lsm_batch_multiple * math.max(
167
+ constants.batch_max.create_accounts,
168
+ 2 * constants.batch_max.create_transfers,
169
+ ),
170
+ .debits_posted = constants_.lsm_batch_multiple * math.max(
171
+ constants.batch_max.create_accounts,
172
+ 2 * constants.batch_max.create_transfers,
173
+ ),
174
+ .credits_pending = constants_.lsm_batch_multiple * math.max(
175
+ constants.batch_max.create_accounts,
176
+ 2 * constants.batch_max.create_transfers,
177
+ ),
178
+ .credits_posted = constants_.lsm_batch_multiple * math.max(
179
+ constants.batch_max.create_accounts,
180
+ 2 * constants.batch_max.create_transfers,
181
+ ),
182
+ },
183
+ .ignored = &[_][]const u8{"padding"},
184
+ .derived = .{},
185
+ },
186
+ );
187
+
42
188
  const TransfersGroove = GrooveType(
43
189
  Storage,
44
190
  Transfer,
45
191
  .{
192
+ .value_count_max = .{
193
+ .timestamp = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
194
+ .id = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
195
+ .debit_account_id = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
196
+ .credit_account_id = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
197
+ .user_data = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
198
+ .pending_id = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
199
+ .timeout = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
200
+ .ledger = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
201
+ .code = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
202
+ .amount = constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
203
+ },
46
204
  .ignored = &[_][]const u8{ "reserved", "flags" },
47
205
  .derived = .{},
48
206
  },
49
207
  );
50
- const PostedGroove = @import("lsm/posted_groove.zig").PostedGrooveType(Storage);
208
+
209
+ const PostedGroove = @import("lsm/posted_groove.zig").PostedGrooveType(
210
+ Storage,
211
+ constants_.lsm_batch_multiple * constants.batch_max.create_transfers,
212
+ );
51
213
 
52
214
  pub const Workload = WorkloadType(StateMachine);
53
215
 
54
216
  pub const Forest = ForestType(Storage, .{
55
- .accounts = AccountsGroove,
217
+ .accounts_immutable = AccountsImmutableGroove,
218
+ .accounts_mutable = AccountsMutableGroove,
56
219
  .transfers = TransfersGroove,
57
220
  .posted = PostedGroove,
58
221
  });
@@ -70,32 +233,6 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
70
233
  lookup_transfers,
71
234
  };
72
235
 
73
- pub const constants = struct {
74
- pub const message_body_size_max = constants_.message_body_size_max;
75
-
76
- /// The maximum number of objects within a batch, by operation.
77
- pub const batch_max = struct {
78
- pub const create_accounts = operation_batch_max(.create_accounts);
79
- pub const create_transfers = operation_batch_max(.create_transfers);
80
- pub const lookup_accounts = operation_batch_max(.lookup_accounts);
81
- pub const lookup_transfers = operation_batch_max(.lookup_transfers);
82
-
83
- comptime {
84
- assert(create_accounts > 0);
85
- assert(create_transfers > 0);
86
- assert(lookup_accounts > 0);
87
- assert(lookup_transfers > 0);
88
- }
89
-
90
- fn operation_batch_max(comptime operation: Operation) usize {
91
- return @divFloor(message_body_size_max, std.math.max(
92
- @sizeOf(Event(operation)),
93
- @sizeOf(Result(operation)),
94
- ));
95
- }
96
- };
97
- };
98
-
99
236
  pub const Options = struct {
100
237
  lsm_forest_node_count: u32,
101
238
  cache_entries_accounts: u32,
@@ -112,7 +249,8 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
112
249
  // TODO(ifreund): use a union for these to save memory, likely an extern union
113
250
  // so that we can safetly @ptrCast() until @fieldParentPtr() is implemented
114
251
  // for unions. See: https://github.com/ziglang/zig/issues/6611
115
- prefetch_accounts_context: AccountsGroove.PrefetchContext = undefined,
252
+ prefetch_accounts_immutable_context: AccountsImmutableGroove.PrefetchContext = undefined,
253
+ prefetch_accounts_mutable_context: AccountsMutableGroove.PrefetchContext = undefined,
116
254
  prefetch_transfers_context: TransfersGroove.PrefetchContext = undefined,
117
255
  prefetch_posted_context: PostedGroove.PrefetchContext = undefined,
118
256
 
@@ -225,7 +363,8 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
225
363
  self.prefetch_callback = callback;
226
364
 
227
365
  // TODO(Snapshots) Pass in the target snapshot.
228
- self.forest.grooves.accounts.prefetch_setup(null);
366
+ self.forest.grooves.accounts_immutable.prefetch_setup(null);
367
+ self.forest.grooves.accounts_mutable.prefetch_setup(null);
229
368
  self.forest.grooves.transfers.prefetch_setup(null);
230
369
  self.forest.grooves.posted.prefetch_setup(null);
231
370
 
@@ -263,16 +402,41 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
263
402
 
264
403
  fn prefetch_create_accounts(self: *StateMachine, accounts: []const Account) void {
265
404
  for (accounts) |*a| {
266
- self.forest.grooves.accounts.prefetch_enqueue(a.id);
405
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(a.id);
267
406
  }
268
- self.forest.grooves.accounts.prefetch(
269
- prefetch_create_accounts_callback,
270
- &self.prefetch_accounts_context,
407
+ self.forest.grooves.accounts_immutable.prefetch(
408
+ prefetch_create_accounts_immutable_callback,
409
+ &self.prefetch_accounts_immutable_context,
271
410
  );
272
411
  }
273
412
 
274
- fn prefetch_create_accounts_callback(completion: *AccountsGroove.PrefetchContext) void {
275
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
413
+ fn prefetch_create_accounts_immutable_callback(
414
+ completion: *AccountsImmutableGroove.PrefetchContext,
415
+ ) void {
416
+ const self = @fieldParentPtr(
417
+ StateMachine,
418
+ "prefetch_accounts_immutable_context",
419
+ completion,
420
+ );
421
+
422
+ // Nothing to prefetch_enqueue() from accounts_mutable as accounts_immutable
423
+ // is all that is needed to check for pre-existing accounts before creating one.
424
+ // We still call prefetch() anyway to keep a valid/expected Groove state for commit().
425
+
426
+ self.forest.grooves.accounts_mutable.prefetch(
427
+ prefetch_create_accounts_mutable_callback,
428
+ &self.prefetch_accounts_mutable_context,
429
+ );
430
+ }
431
+
432
+ fn prefetch_create_accounts_mutable_callback(
433
+ completion: *AccountsMutableGroove.PrefetchContext,
434
+ ) void {
435
+ const self = @fieldParentPtr(
436
+ StateMachine,
437
+ "prefetch_accounts_mutable_context",
438
+ completion,
439
+ );
276
440
 
277
441
  self.prefetch_finish();
278
442
  }
@@ -302,23 +466,53 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
302
466
  for (transfers) |*t| {
303
467
  if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
304
468
  if (self.forest.grooves.transfers.get(t.pending_id)) |p| {
305
- self.forest.grooves.accounts.prefetch_enqueue(p.debit_account_id);
306
- self.forest.grooves.accounts.prefetch_enqueue(p.credit_account_id);
469
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(p.debit_account_id);
470
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(p.credit_account_id);
471
+ }
472
+ } else {
473
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(t.debit_account_id);
474
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(t.credit_account_id);
475
+ }
476
+ }
477
+
478
+ self.forest.grooves.accounts_immutable.prefetch(
479
+ prefetch_create_transfers_callback_accounts_immutable,
480
+ &self.prefetch_accounts_immutable_context,
481
+ );
482
+ }
483
+
484
+ fn prefetch_create_transfers_callback_accounts_immutable(completion: *AccountsImmutableGroove.PrefetchContext) void {
485
+ const self = @fieldParentPtr(StateMachine, "prefetch_accounts_immutable_context", completion);
486
+
487
+ const transfers = mem.bytesAsSlice(Event(.create_transfers), self.prefetch_input.?);
488
+ for (transfers) |*t| {
489
+ if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
490
+ if (self.forest.grooves.transfers.get(t.pending_id)) |p| {
491
+ if (self.forest.grooves.accounts_immutable.get(p.debit_account_id)) |dr_immut| {
492
+ self.forest.grooves.accounts_mutable.prefetch_enqueue(dr_immut.timestamp);
493
+ }
494
+ if (self.forest.grooves.accounts_immutable.get(p.credit_account_id)) |cr_immut| {
495
+ self.forest.grooves.accounts_mutable.prefetch_enqueue(cr_immut.timestamp);
496
+ }
307
497
  }
308
498
  } else {
309
- self.forest.grooves.accounts.prefetch_enqueue(t.debit_account_id);
310
- self.forest.grooves.accounts.prefetch_enqueue(t.credit_account_id);
499
+ if (self.forest.grooves.accounts_immutable.get(t.debit_account_id)) |dr_immut| {
500
+ self.forest.grooves.accounts_mutable.prefetch_enqueue(dr_immut.timestamp);
501
+ }
502
+ if (self.forest.grooves.accounts_immutable.get(t.credit_account_id)) |cr_immut| {
503
+ self.forest.grooves.accounts_mutable.prefetch_enqueue(cr_immut.timestamp);
504
+ }
311
505
  }
312
506
  }
313
507
 
314
- self.forest.grooves.accounts.prefetch(
315
- prefetch_create_transfers_callback_accounts,
316
- &self.prefetch_accounts_context,
508
+ self.forest.grooves.accounts_mutable.prefetch(
509
+ prefetch_create_transfers_callback_accounts_mutable,
510
+ &self.prefetch_accounts_mutable_context,
317
511
  );
318
512
  }
319
513
 
320
- fn prefetch_create_transfers_callback_accounts(completion: *AccountsGroove.PrefetchContext) void {
321
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
514
+ fn prefetch_create_transfers_callback_accounts_mutable(completion: *AccountsMutableGroove.PrefetchContext) void {
515
+ const self = @fieldParentPtr(StateMachine, "prefetch_accounts_mutable_context", completion);
322
516
 
323
517
  self.forest.grooves.posted.prefetch(
324
518
  prefetch_create_transfers_callback_posted,
@@ -334,17 +528,33 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
334
528
 
335
529
  fn prefetch_lookup_accounts(self: *StateMachine, ids: []const u128) void {
336
530
  for (ids) |id| {
337
- self.forest.grooves.accounts.prefetch_enqueue(id);
531
+ self.forest.grooves.accounts_immutable.prefetch_enqueue(id);
338
532
  }
339
533
 
340
- self.forest.grooves.accounts.prefetch(
341
- prefetch_lookup_accounts_callback,
342
- &self.prefetch_accounts_context,
534
+ self.forest.grooves.accounts_immutable.prefetch(
535
+ prefetch_lookup_accounts_immutable_callback,
536
+ &self.prefetch_accounts_immutable_context,
343
537
  );
344
538
  }
345
539
 
346
- fn prefetch_lookup_accounts_callback(completion: *AccountsGroove.PrefetchContext) void {
347
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
540
+ fn prefetch_lookup_accounts_immutable_callback(completion: *AccountsImmutableGroove.PrefetchContext) void {
541
+ const self = @fieldParentPtr(StateMachine, "prefetch_accounts_immutable_context", completion);
542
+
543
+ const ids = mem.bytesAsSlice(Event(.lookup_accounts), self.prefetch_input.?);
544
+ for (ids) |id| {
545
+ if (self.forest.grooves.accounts_immutable.get(id)) |immut| {
546
+ self.forest.grooves.accounts_mutable.prefetch_enqueue(immut.timestamp);
547
+ }
548
+ }
549
+
550
+ self.forest.grooves.accounts_mutable.prefetch(
551
+ prefetch_lookup_accounts_mutable_callback,
552
+ &self.prefetch_accounts_mutable_context,
553
+ );
554
+ }
555
+
556
+ fn prefetch_lookup_accounts_mutable_callback(completion: *AccountsMutableGroove.PrefetchContext) void {
557
+ const self = @fieldParentPtr(StateMachine, "prefetch_accounts_mutable_context", completion);
348
558
 
349
559
  self.prefetch_finish();
350
560
  }
@@ -577,8 +787,9 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
577
787
  const results = mem.bytesAsSlice(Account, output[0..output_len]);
578
788
  var results_count: usize = 0;
579
789
  for (batch) |id| {
580
- if (self.get_account(id)) |result| {
581
- results[results_count] = result.*;
790
+ if (self.forest.grooves.accounts_immutable.get(id)) |immut| {
791
+ const mut = self.forest.grooves.accounts_mutable.get(immut.timestamp).?;
792
+ results[results_count] = into_account(immut, mut);
582
793
  results_count += 1;
583
794
  }
584
795
  }
@@ -624,23 +835,30 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
624
835
  if (a.credits_pending != 0) return .credits_pending_must_be_zero;
625
836
  if (a.credits_posted != 0) return .credits_posted_must_be_zero;
626
837
 
627
- if (self.get_account(a.id)) |e| return create_account_exists(a, e);
838
+ if (self.forest.grooves.accounts_immutable.get(a.id)) |e| {
839
+ return create_account_exists(a, e);
840
+ }
628
841
 
629
- self.forest.grooves.accounts.put_no_clobber(a);
842
+ self.forest.grooves.accounts_immutable.put_no_clobber(&AccountImmutable.from_account(a));
843
+ self.forest.grooves.accounts_mutable.put_no_clobber(&AccountMutable.from_account(a));
630
844
 
631
845
  self.commit_timestamp = a.timestamp;
632
846
  return .ok;
633
847
  }
634
848
 
635
849
  fn create_account_rollback(self: *StateMachine, a: *const Account) void {
636
- self.forest.grooves.accounts.remove(a.id);
850
+ // Need to get the timestamp from the inserted account rather than the one passed in.
851
+ const timestamp = self.forest.grooves.accounts_immutable.get(a.id).?.timestamp;
852
+
853
+ self.forest.grooves.accounts_immutable.remove(a.id);
854
+ self.forest.grooves.accounts_mutable.remove(timestamp);
637
855
  }
638
856
 
639
- fn create_account_exists(a: *const Account, e: *const Account) CreateAccountResult {
857
+ fn create_account_exists(a: *const Account, e: *const AccountImmutable) CreateAccountResult {
640
858
  assert(a.id == e.id);
641
859
  if (@bitCast(u16, a.flags) != @bitCast(u16, e.flags)) return .exists_with_different_flags;
642
860
  if (a.user_data != e.user_data) return .exists_with_different_user_data;
643
- assert(zeroed_48_bytes(a.reserved) and zeroed_48_bytes(e.reserved));
861
+ assert(zeroed_48_bytes(a.reserved) and zeroed_16_bytes(e.padding));
644
862
  if (a.ledger != e.ledger) return .exists_with_different_ledger;
645
863
  if (a.code != e.code) return .exists_with_different_code;
646
864
  return .exists;
@@ -679,50 +897,55 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
679
897
  // 2. standing for debit record and credit record, or
680
898
  // 3. relating to debtor and creditor.
681
899
  // We use them to distinguish between `cr` (credit account), and `c` (commit).
682
- const dr = self.get_account(t.debit_account_id) orelse return .debit_account_not_found;
683
- const cr = self.get_account(t.credit_account_id) orelse return .credit_account_not_found;
684
- assert(dr.id == t.debit_account_id);
685
- assert(cr.id == t.credit_account_id);
686
- assert(t.timestamp > dr.timestamp);
687
- assert(t.timestamp > cr.timestamp);
900
+ const dr_immut = self.forest.grooves.accounts_immutable.get(t.debit_account_id) orelse return .debit_account_not_found;
901
+ const cr_immut = self.forest.grooves.accounts_immutable.get(t.credit_account_id) orelse return .credit_account_not_found;
902
+ assert(dr_immut.id == t.debit_account_id);
903
+ assert(cr_immut.id == t.credit_account_id);
904
+ assert(t.timestamp > dr_immut.timestamp);
905
+ assert(t.timestamp > cr_immut.timestamp);
688
906
 
689
- if (dr.ledger != cr.ledger) return .accounts_must_have_the_same_ledger;
690
- if (t.ledger != dr.ledger) return .transfer_must_have_the_same_ledger_as_accounts;
907
+ if (dr_immut.ledger != cr_immut.ledger) return .accounts_must_have_the_same_ledger;
908
+ if (t.ledger != dr_immut.ledger) return .transfer_must_have_the_same_ledger_as_accounts;
691
909
 
692
910
  // If the transfer already exists, then it must not influence the overflow or limit checks.
693
911
  if (self.get_transfer(t.id)) |e| return create_transfer_exists(t, e);
694
912
 
913
+ const dr_mut = self.forest.grooves.accounts_mutable.get(dr_immut.timestamp).?;
914
+ const cr_mut = self.forest.grooves.accounts_mutable.get(cr_immut.timestamp).?;
915
+ assert(dr_mut.timestamp == dr_immut.timestamp);
916
+ assert(cr_mut.timestamp == cr_immut.timestamp);
917
+
695
918
  if (t.flags.pending) {
696
- if (sum_overflows(t.amount, dr.debits_pending)) return .overflows_debits_pending;
697
- if (sum_overflows(t.amount, cr.credits_pending)) return .overflows_credits_pending;
919
+ if (sum_overflows(t.amount, dr_mut.debits_pending)) return .overflows_debits_pending;
920
+ if (sum_overflows(t.amount, cr_mut.credits_pending)) return .overflows_credits_pending;
698
921
  }
699
- if (sum_overflows(t.amount, dr.debits_posted)) return .overflows_debits_posted;
700
- if (sum_overflows(t.amount, cr.credits_posted)) return .overflows_credits_posted;
922
+ if (sum_overflows(t.amount, dr_mut.debits_posted)) return .overflows_debits_posted;
923
+ if (sum_overflows(t.amount, cr_mut.credits_posted)) return .overflows_credits_posted;
701
924
  // We assert that the sum of the pending and posted balances can never overflow:
702
- if (sum_overflows(t.amount, dr.debits_pending + dr.debits_posted)) {
925
+ if (sum_overflows(t.amount, dr_mut.debits_pending + dr_mut.debits_posted)) {
703
926
  return .overflows_debits;
704
927
  }
705
- if (sum_overflows(t.amount, cr.credits_pending + cr.credits_posted)) {
928
+ if (sum_overflows(t.amount, cr_mut.credits_pending + cr_mut.credits_posted)) {
706
929
  return .overflows_credits;
707
930
  }
708
931
  if (sum_overflows(t.timestamp, t.timeout)) return .overflows_timeout;
709
932
 
710
- if (dr.debits_exceed_credits(t.amount)) return .exceeds_credits;
711
- if (cr.credits_exceed_debits(t.amount)) return .exceeds_debits;
933
+ if (into_account(dr_immut, dr_mut).debits_exceed_credits(t.amount)) return .exceeds_credits;
934
+ if (into_account(cr_immut, cr_mut).credits_exceed_debits(t.amount)) return .exceeds_debits;
712
935
 
713
936
  self.forest.grooves.transfers.put_no_clobber(t);
714
937
 
715
- var dr_new = dr.*;
716
- var cr_new = cr.*;
938
+ var dr_mut_new = dr_mut.*;
939
+ var cr_mut_new = cr_mut.*;
717
940
  if (t.flags.pending) {
718
- dr_new.debits_pending += t.amount;
719
- cr_new.credits_pending += t.amount;
941
+ dr_mut_new.debits_pending += t.amount;
942
+ cr_mut_new.credits_pending += t.amount;
720
943
  } else {
721
- dr_new.debits_posted += t.amount;
722
- cr_new.credits_posted += t.amount;
944
+ dr_mut_new.debits_posted += t.amount;
945
+ cr_mut_new.credits_posted += t.amount;
723
946
  }
724
- self.forest.grooves.accounts.put(&dr_new);
725
- self.forest.grooves.accounts.put(&cr_new);
947
+ self.forest.grooves.accounts_mutable.put(&dr_mut_new);
948
+ self.forest.grooves.accounts_mutable.put(&cr_mut_new);
726
949
 
727
950
  self.commit_timestamp = t.timestamp;
728
951
  return .ok;
@@ -733,20 +956,25 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
733
956
  return self.post_or_void_pending_transfer_rollback(t);
734
957
  }
735
958
 
736
- var dr = self.get_account(t.debit_account_id).?.*;
737
- var cr = self.get_account(t.credit_account_id).?.*;
738
- assert(dr.id == t.debit_account_id);
739
- assert(cr.id == t.credit_account_id);
959
+ const dr_immut = self.forest.grooves.accounts_immutable.get(t.debit_account_id).?;
960
+ const cr_immut = self.forest.grooves.accounts_immutable.get(t.credit_account_id).?;
961
+ assert(dr_immut.id == t.debit_account_id);
962
+ assert(cr_immut.id == t.credit_account_id);
963
+
964
+ var dr_mut = self.forest.grooves.accounts_mutable.get(dr_immut.timestamp).?.*;
965
+ var cr_mut = self.forest.grooves.accounts_mutable.get(cr_immut.timestamp).?.*;
966
+ assert(dr_mut.timestamp == dr_immut.timestamp);
967
+ assert(cr_mut.timestamp == cr_immut.timestamp);
740
968
 
741
969
  if (t.flags.pending) {
742
- dr.debits_pending -= t.amount;
743
- cr.credits_pending -= t.amount;
970
+ dr_mut.debits_pending -= t.amount;
971
+ cr_mut.credits_pending -= t.amount;
744
972
  } else {
745
- dr.debits_posted -= t.amount;
746
- cr.credits_posted -= t.amount;
973
+ dr_mut.debits_posted -= t.amount;
974
+ cr_mut.credits_posted -= t.amount;
747
975
  }
748
- self.forest.grooves.accounts.put(&dr);
749
- self.forest.grooves.accounts.put(&cr);
976
+ self.forest.grooves.accounts_mutable.put(&dr_mut);
977
+ self.forest.grooves.accounts_mutable.put(&cr_mut);
750
978
 
751
979
  self.forest.grooves.transfers.remove(t.id);
752
980
  }
@@ -792,12 +1020,12 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
792
1020
  assert(p.id == t.pending_id);
793
1021
  if (!p.flags.pending) return .pending_transfer_not_pending;
794
1022
 
795
- const dr = self.get_account(p.debit_account_id).?;
796
- const cr = self.get_account(p.credit_account_id).?;
797
- assert(dr.id == p.debit_account_id);
798
- assert(cr.id == p.credit_account_id);
799
- assert(p.timestamp > dr.timestamp);
800
- assert(p.timestamp > cr.timestamp);
1023
+ const dr_immut = self.forest.grooves.accounts_immutable.get(p.debit_account_id).?;
1024
+ const cr_immut = self.forest.grooves.accounts_immutable.get(p.credit_account_id).?;
1025
+ assert(dr_immut.id == p.debit_account_id);
1026
+ assert(cr_immut.id == p.credit_account_id);
1027
+ assert(p.timestamp > dr_immut.timestamp);
1028
+ assert(p.timestamp > cr_immut.timestamp);
801
1029
  assert(p.amount > 0);
802
1030
 
803
1031
  if (t.debit_account_id > 0 and t.debit_account_id != p.debit_account_id) {
@@ -846,21 +1074,23 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
846
1074
 
847
1075
  self.forest.grooves.posted.put_no_clobber(t.pending_id, t.flags.post_pending_transfer);
848
1076
 
849
- var dr_new = dr.*;
850
- var cr_new = cr.*;
1077
+ var dr_mut_new = self.forest.grooves.accounts_mutable.get(dr_immut.timestamp).?.*;
1078
+ var cr_mut_new = self.forest.grooves.accounts_mutable.get(cr_immut.timestamp).?.*;
1079
+ assert(dr_mut_new.timestamp == dr_immut.timestamp);
1080
+ assert(cr_mut_new.timestamp == cr_immut.timestamp);
851
1081
 
852
- dr_new.debits_pending -= p.amount;
853
- cr_new.credits_pending -= p.amount;
1082
+ dr_mut_new.debits_pending -= p.amount;
1083
+ cr_mut_new.credits_pending -= p.amount;
854
1084
 
855
1085
  if (t.flags.post_pending_transfer) {
856
1086
  assert(amount > 0);
857
1087
  assert(amount <= p.amount);
858
- dr_new.debits_posted += amount;
859
- cr_new.credits_posted += amount;
1088
+ dr_mut_new.debits_posted += amount;
1089
+ cr_mut_new.credits_posted += amount;
860
1090
  }
861
1091
 
862
- self.forest.grooves.accounts.put(&dr_new);
863
- self.forest.grooves.accounts.put(&cr_new);
1092
+ self.forest.grooves.accounts_mutable.put(&dr_mut_new);
1093
+ self.forest.grooves.accounts_mutable.put(&cr_mut_new);
864
1094
 
865
1095
  self.commit_timestamp = t.timestamp;
866
1096
  return .ok;
@@ -876,23 +1106,28 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
876
1106
  assert(p.debit_account_id > 0);
877
1107
  assert(p.credit_account_id > 0);
878
1108
 
879
- var dr = self.get_account(p.debit_account_id).?.*;
880
- var cr = self.get_account(p.credit_account_id).?.*;
881
- assert(dr.id == p.debit_account_id);
882
- assert(cr.id == p.credit_account_id);
1109
+ const dr_immut = self.forest.grooves.accounts_immutable.get(p.debit_account_id).?;
1110
+ const cr_immut = self.forest.grooves.accounts_immutable.get(p.credit_account_id).?;
1111
+ assert(dr_immut.id == p.debit_account_id);
1112
+ assert(cr_immut.id == p.credit_account_id);
1113
+
1114
+ var dr_mut = self.forest.grooves.accounts_mutable.get(dr_immut.timestamp).?.*;
1115
+ var cr_mut = self.forest.grooves.accounts_mutable.get(cr_immut.timestamp).?.*;
1116
+ assert(dr_mut.timestamp == dr_immut.timestamp);
1117
+ assert(cr_mut.timestamp == cr_immut.timestamp);
883
1118
 
884
1119
  if (t.flags.post_pending_transfer) {
885
1120
  const amount = if (t.amount > 0) t.amount else p.amount;
886
1121
  assert(amount > 0);
887
1122
  assert(amount <= p.amount);
888
- dr.debits_posted -= amount;
889
- cr.credits_posted -= amount;
1123
+ dr_mut.debits_posted -= amount;
1124
+ cr_mut.credits_posted -= amount;
890
1125
  }
891
- dr.debits_pending += p.amount;
892
- cr.credits_pending += p.amount;
1126
+ dr_mut.debits_pending += p.amount;
1127
+ cr_mut.credits_pending += p.amount;
893
1128
 
894
- self.forest.grooves.accounts.put(&dr);
895
- self.forest.grooves.accounts.put(&cr);
1129
+ self.forest.grooves.accounts_mutable.put(&dr_mut);
1130
+ self.forest.grooves.accounts_mutable.put(&cr_mut);
896
1131
 
897
1132
  self.forest.grooves.posted.remove(t.pending_id);
898
1133
  self.forest.grooves.transfers.remove(t.id);
@@ -951,10 +1186,6 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
951
1186
  return .exists;
952
1187
  }
953
1188
 
954
- fn get_account(self: *const StateMachine, id: u128) ?*const Account {
955
- return self.forest.grooves.accounts.get(id);
956
- }
957
-
958
1189
  fn get_transfer(self: *const StateMachine, id: u128) ?*const Transfer {
959
1190
  return self.forest.grooves.transfers.get(id);
960
1191
  }
@@ -971,60 +1202,43 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
971
1202
  assert(batch_transfers_max == constants.batch_max.lookup_transfers);
972
1203
 
973
1204
  return .{
974
- .accounts = .{
1205
+ .accounts_immutable = .{
975
1206
  .prefetch_entries_max = std.math.max(
976
- // create_account()/lookup_account() looks up 1 account per item.
1207
+ // create_account()/lookup_account() looks up 1 AccountImmutable per item.
977
1208
  batch_accounts_max,
978
1209
  // create_transfer()/post_or_void_pending_transfer() looks up 2
979
- // accounts for every transfer.
1210
+ // AccountImmutables for every transfer.
980
1211
  2 * batch_transfers_max,
981
1212
  ),
982
1213
  .tree_options_object = .{
983
1214
  .cache_entries_max = options.cache_entries_accounts,
984
- .commit_entries_max = math.max(
985
- batch_accounts_max,
986
- // ×2 because creating a transfer will update 2 accounts.
987
- 2 * batch_transfers_max,
988
- ),
989
1215
  },
990
1216
  .tree_options_id = .{
991
1217
  .cache_entries_max = options.cache_entries_accounts,
992
- .commit_entries_max = batch_accounts_max,
993
1218
  },
994
1219
  .tree_options_index = .{
995
- .user_data = .{ .commit_entries_max = batch_accounts_max },
996
- .ledger = .{ .commit_entries_max = batch_accounts_max },
997
- .code = .{ .commit_entries_max = batch_accounts_max },
998
- // Transfers mutate the secondary indices for debits/credits pending/posted.
999
- //
1000
- // * Each mutation results in a remove and an insert: the ×2 multiplier.
1001
- // * Each transfer modifies two accounts. However, this does not
1002
- // necessitate an additional ×2 multiplier the credits of the debit
1003
- // account and the debits of the credit account are not modified.
1004
- .debits_pending = .{
1005
- .commit_entries_max = math.max(
1006
- batch_accounts_max,
1007
- 2 * batch_transfers_max,
1008
- ),
1009
- },
1010
- .debits_posted = .{
1011
- .commit_entries_max = math.max(
1012
- batch_accounts_max,
1013
- 2 * batch_transfers_max,
1014
- ),
1015
- },
1016
- .credits_pending = .{
1017
- .commit_entries_max = math.max(
1018
- batch_accounts_max,
1019
- 2 * batch_transfers_max,
1020
- ),
1021
- },
1022
- .credits_posted = .{
1023
- .commit_entries_max = math.max(
1024
- batch_accounts_max,
1025
- 2 * batch_transfers_max,
1026
- ),
1027
- },
1220
+ .user_data = .{},
1221
+ .ledger = .{},
1222
+ .code = .{},
1223
+ },
1224
+ },
1225
+ .accounts_mutable = .{
1226
+ .prefetch_entries_max = std.math.max(
1227
+ // create_account()/lookup_account() looks up 1 AccountMutable per item.
1228
+ batch_accounts_max,
1229
+ // create_transfer()/post_or_void_pending_transfer() looks up 2
1230
+ // AccountMutables for every transfer.
1231
+ 2 * batch_transfers_max,
1232
+ ),
1233
+ .tree_options_object = .{
1234
+ .cache_entries_max = options.cache_entries_accounts,
1235
+ },
1236
+ .tree_options_id = {}, // No ID tree at there's one already for AccountsMutable.
1237
+ .tree_options_index = .{
1238
+ .debits_pending = .{},
1239
+ .debits_posted = .{},
1240
+ .credits_pending = .{},
1241
+ .credits_posted = .{},
1028
1242
  },
1029
1243
  },
1030
1244
  .transfers = .{
@@ -1032,27 +1246,24 @@ pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
1032
1246
  .prefetch_entries_max = 2 * batch_transfers_max,
1033
1247
  .tree_options_object = .{
1034
1248
  .cache_entries_max = options.cache_entries_transfers,
1035
- .commit_entries_max = batch_transfers_max,
1036
1249
  },
1037
1250
  .tree_options_id = .{
1038
1251
  .cache_entries_max = options.cache_entries_transfers,
1039
- .commit_entries_max = batch_transfers_max,
1040
1252
  },
1041
1253
  .tree_options_index = .{
1042
- .debit_account_id = .{ .commit_entries_max = batch_transfers_max },
1043
- .credit_account_id = .{ .commit_entries_max = batch_transfers_max },
1044
- .user_data = .{ .commit_entries_max = batch_transfers_max },
1045
- .pending_id = .{ .commit_entries_max = batch_transfers_max },
1046
- .timeout = .{ .commit_entries_max = batch_transfers_max },
1047
- .ledger = .{ .commit_entries_max = batch_transfers_max },
1048
- .code = .{ .commit_entries_max = batch_transfers_max },
1049
- .amount = .{ .commit_entries_max = batch_transfers_max },
1254
+ .debit_account_id = .{},
1255
+ .credit_account_id = .{},
1256
+ .user_data = .{},
1257
+ .pending_id = .{},
1258
+ .timeout = .{},
1259
+ .ledger = .{},
1260
+ .code = .{},
1261
+ .amount = .{},
1050
1262
  },
1051
1263
  },
1052
1264
  .posted = .{
1053
1265
  .cache_entries_max = options.cache_entries_posted,
1054
1266
  .prefetch_entries_max = batch_transfers_max,
1055
- .commit_entries_max = batch_transfers_max,
1056
1267
  },
1057
1268
  };
1058
1269
  }
@@ -1065,6 +1276,11 @@ fn sum_overflows(a: u64, b: u64) bool {
1065
1276
  }
1066
1277
 
1067
1278
  /// Optimizes for the common case, where the array is zeroed. Completely branchless.
1279
+ fn zeroed_16_bytes(a: [16]u8) bool {
1280
+ const x = @bitCast([2]u64, a);
1281
+ return (x[0] | x[1]) == 0;
1282
+ }
1283
+
1068
1284
  fn zeroed_32_bytes(a: [32]u8) bool {
1069
1285
  const x = @bitCast([4]u64, a);
1070
1286
  return (x[0] | x[1] | x[2] | x[3]) == 0;
@@ -1121,9 +1337,9 @@ const TestContext = struct {
1121
1337
  const SuperBlock = @import("vsr/superblock.zig").SuperBlockType(Storage);
1122
1338
  const Grid = @import("lsm/grid.zig").GridType(Storage);
1123
1339
  const StateMachine = StateMachineType(Storage, .{
1124
- // Overestimate the batch size (in order to overprovision commit_entries_max)
1125
- // because the test never compacts.
1340
+ // Overestimate the batch size because the test never compacts.
1126
1341
  .message_body_size_max = message_body_size_max,
1342
+ .lsm_batch_multiple = 1,
1127
1343
  });
1128
1344
  const message_body_size_max = 32 * @sizeOf(Account);
1129
1345
 
@@ -1328,12 +1544,13 @@ fn check(comptime test_table: []const u8) !void {
1328
1544
  .setup => |b| {
1329
1545
  assert(operation == null);
1330
1546
 
1331
- var account = context.state_machine.get_account(b.account).?.*;
1332
- account.debits_pending = b.debits_pending;
1333
- account.debits_posted = b.debits_posted;
1334
- account.credits_pending = b.credits_pending;
1335
- account.credits_posted = b.credits_posted;
1336
- context.state_machine.forest.grooves.accounts.put(&account);
1547
+ const immut = context.state_machine.forest.grooves.accounts_immutable.get(b.account).?;
1548
+ var mut = context.state_machine.forest.grooves.accounts_mutable.get(immut.timestamp).?.*;
1549
+ mut.debits_pending = b.debits_pending;
1550
+ mut.debits_posted = b.debits_posted;
1551
+ mut.credits_pending = b.credits_pending;
1552
+ mut.credits_posted = b.credits_posted;
1553
+ context.state_machine.forest.grooves.accounts_mutable.put(&mut);
1337
1554
  },
1338
1555
 
1339
1556
  .account => |a| {
@@ -1875,6 +2092,7 @@ test "StateMachine: ref all decls" {
1875
2092
  const Storage = @import("storage.zig").Storage;
1876
2093
  const StateMachine = StateMachineType(Storage, .{
1877
2094
  .message_body_size_max = 1000 * @sizeOf(Account),
2095
+ .lsm_batch_multiple = 1,
1878
2096
  });
1879
2097
 
1880
2098
  std.testing.refAllDecls(StateMachine);