tigerbeetle-node 0.11.12 → 0.12.0

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 (143) hide show
  1. package/README.md +212 -196
  2. package/dist/bin/aarch64-linux-gnu/client.node +0 -0
  3. package/dist/bin/aarch64-linux-musl/client.node +0 -0
  4. package/dist/bin/aarch64-macos/client.node +0 -0
  5. package/dist/bin/x86_64-linux-gnu/client.node +0 -0
  6. package/dist/bin/x86_64-linux-musl/client.node +0 -0
  7. package/dist/bin/x86_64-macos/client.node +0 -0
  8. package/dist/index.js +33 -1
  9. package/dist/index.js.map +1 -1
  10. package/package-lock.json +66 -0
  11. package/package.json +8 -17
  12. package/src/index.ts +56 -1
  13. package/src/node.zig +10 -9
  14. package/dist/.client.node.sha256 +0 -1
  15. package/scripts/build_lib.sh +0 -61
  16. package/scripts/download_node_headers.sh +0 -32
  17. package/src/tigerbeetle/scripts/benchmark.bat +0 -48
  18. package/src/tigerbeetle/scripts/benchmark.sh +0 -66
  19. package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
  20. package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
  21. package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
  22. package/src/tigerbeetle/scripts/install.bat +0 -7
  23. package/src/tigerbeetle/scripts/install.sh +0 -21
  24. package/src/tigerbeetle/scripts/install_zig.bat +0 -113
  25. package/src/tigerbeetle/scripts/install_zig.sh +0 -90
  26. package/src/tigerbeetle/scripts/lint.zig +0 -199
  27. package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
  28. package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -48
  29. package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
  30. package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
  31. package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
  32. package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
  33. package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
  34. package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
  35. package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
  36. package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
  37. package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
  38. package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
  39. package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
  40. package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
  41. package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
  42. package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
  43. package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
  44. package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
  45. package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
  46. package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
  47. package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
  48. package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
  49. package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
  50. package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
  51. package/src/tigerbeetle/src/benchmark.zig +0 -314
  52. package/src/tigerbeetle/src/config.zig +0 -234
  53. package/src/tigerbeetle/src/constants.zig +0 -436
  54. package/src/tigerbeetle/src/ewah.zig +0 -286
  55. package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
  56. package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
  57. package/src/tigerbeetle/src/fifo.zig +0 -120
  58. package/src/tigerbeetle/src/io/benchmark.zig +0 -213
  59. package/src/tigerbeetle/src/io/darwin.zig +0 -814
  60. package/src/tigerbeetle/src/io/linux.zig +0 -1062
  61. package/src/tigerbeetle/src/io/test.zig +0 -643
  62. package/src/tigerbeetle/src/io/windows.zig +0 -1183
  63. package/src/tigerbeetle/src/io.zig +0 -34
  64. package/src/tigerbeetle/src/iops.zig +0 -107
  65. package/src/tigerbeetle/src/lsm/README.md +0 -308
  66. package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
  67. package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
  68. package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
  69. package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
  70. package/src/tigerbeetle/src/lsm/direction.zig +0 -11
  71. package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
  72. package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
  73. package/src/tigerbeetle/src/lsm/forest.zig +0 -204
  74. package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -401
  75. package/src/tigerbeetle/src/lsm/grid.zig +0 -573
  76. package/src/tigerbeetle/src/lsm/groove.zig +0 -972
  77. package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
  78. package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
  79. package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
  80. package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -877
  81. package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
  82. package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
  83. package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
  84. package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
  85. package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -378
  86. package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1328
  87. package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
  88. package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
  89. package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
  90. package/src/tigerbeetle/src/lsm/table.zig +0 -1031
  91. package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -203
  92. package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
  93. package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -220
  94. package/src/tigerbeetle/src/lsm/test.zig +0 -438
  95. package/src/tigerbeetle/src/lsm/tree.zig +0 -1193
  96. package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -474
  97. package/src/tigerbeetle/src/message_bus.zig +0 -1012
  98. package/src/tigerbeetle/src/message_pool.zig +0 -156
  99. package/src/tigerbeetle/src/ring_buffer.zig +0 -399
  100. package/src/tigerbeetle/src/simulator.zig +0 -569
  101. package/src/tigerbeetle/src/state_machine/auditor.zig +0 -577
  102. package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
  103. package/src/tigerbeetle/src/state_machine.zig +0 -1881
  104. package/src/tigerbeetle/src/static_allocator.zig +0 -65
  105. package/src/tigerbeetle/src/stdx.zig +0 -162
  106. package/src/tigerbeetle/src/storage.zig +0 -393
  107. package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
  108. package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
  109. package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
  110. package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
  111. package/src/tigerbeetle/src/testing/cluster.zig +0 -443
  112. package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
  113. package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
  114. package/src/tigerbeetle/src/testing/id.zig +0 -99
  115. package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -364
  116. package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
  117. package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
  118. package/src/tigerbeetle/src/testing/state_machine.zig +0 -249
  119. package/src/tigerbeetle/src/testing/storage.zig +0 -757
  120. package/src/tigerbeetle/src/testing/table.zig +0 -247
  121. package/src/tigerbeetle/src/testing/time.zig +0 -84
  122. package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
  123. package/src/tigerbeetle/src/time.zig +0 -112
  124. package/src/tigerbeetle/src/tracer.zig +0 -529
  125. package/src/tigerbeetle/src/unit_tests.zig +0 -42
  126. package/src/tigerbeetle/src/vopr.zig +0 -495
  127. package/src/tigerbeetle/src/vsr/README.md +0 -209
  128. package/src/tigerbeetle/src/vsr/client.zig +0 -544
  129. package/src/tigerbeetle/src/vsr/clock.zig +0 -853
  130. package/src/tigerbeetle/src/vsr/journal.zig +0 -2413
  131. package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
  132. package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
  133. package/src/tigerbeetle/src/vsr/replica.zig +0 -6381
  134. package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
  135. package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
  136. package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
  137. package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
  138. package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
  139. package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
  140. package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
  141. package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
  142. package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
  143. package/src/tigerbeetle/src/vsr.zig +0 -1352
@@ -1,1881 +0,0 @@
1
- const std = @import("std");
2
- const assert = std.debug.assert;
3
- const math = std.math;
4
- const mem = std.mem;
5
-
6
- const log = std.log.scoped(.state_machine);
7
- const tracer = @import("tracer.zig");
8
-
9
- const tb = @import("tigerbeetle.zig");
10
- const snapshot_latest = @import("lsm/tree.zig").snapshot_latest;
11
- const WorkloadType = @import("state_machine/workload.zig").WorkloadType;
12
-
13
- const Account = tb.Account;
14
- const AccountFlags = tb.AccountFlags;
15
-
16
- const Transfer = tb.Transfer;
17
- const TransferFlags = tb.TransferFlags;
18
-
19
- const CreateAccountsResult = tb.CreateAccountsResult;
20
- const CreateTransfersResult = tb.CreateTransfersResult;
21
-
22
- const CreateAccountResult = tb.CreateAccountResult;
23
- const CreateTransferResult = tb.CreateTransferResult;
24
-
25
- pub fn StateMachineType(comptime Storage: type, comptime constants_: struct {
26
- message_body_size_max: usize,
27
- }) type {
28
- return struct {
29
- const StateMachine = @This();
30
- const Grid = @import("lsm/grid.zig").GridType(Storage);
31
- const GrooveType = @import("lsm/groove.zig").GrooveType;
32
- const ForestType = @import("lsm/forest.zig").ForestType;
33
-
34
- const AccountsGroove = GrooveType(
35
- Storage,
36
- Account,
37
- .{
38
- .ignored = &[_][]const u8{ "reserved", "flags" },
39
- .derived = .{},
40
- },
41
- );
42
- const TransfersGroove = GrooveType(
43
- Storage,
44
- Transfer,
45
- .{
46
- .ignored = &[_][]const u8{ "reserved", "flags" },
47
- .derived = .{},
48
- },
49
- );
50
- const PostedGroove = @import("lsm/posted_groove.zig").PostedGrooveType(Storage);
51
-
52
- pub const Workload = WorkloadType(StateMachine);
53
-
54
- pub const Forest = ForestType(Storage, .{
55
- .accounts = AccountsGroove,
56
- .transfers = TransfersGroove,
57
- .posted = PostedGroove,
58
- });
59
-
60
- pub const Operation = enum(u8) {
61
- /// Operations reserved by VR protocol (for all state machines):
62
- reserved,
63
- root,
64
- register,
65
-
66
- /// Operations exported by TigerBeetle:
67
- create_accounts,
68
- create_transfers,
69
- lookup_accounts,
70
- lookup_transfers,
71
- };
72
-
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
- pub const Options = struct {
100
- lsm_forest_node_count: u32,
101
- cache_entries_accounts: u32,
102
- cache_entries_transfers: u32,
103
- cache_entries_posted: u32,
104
- };
105
-
106
- prepare_timestamp: u64,
107
- commit_timestamp: u64,
108
- forest: Forest,
109
-
110
- prefetch_input: ?[]align(16) const u8 = null,
111
- prefetch_callback: ?fn (*StateMachine) void = null,
112
- // TODO(ifreund): use a union for these to save memory, likely an extern union
113
- // so that we can safetly @ptrCast() until @fieldParentPtr() is implemented
114
- // for unions. See: https://github.com/ziglang/zig/issues/6611
115
- prefetch_accounts_context: AccountsGroove.PrefetchContext = undefined,
116
- prefetch_transfers_context: TransfersGroove.PrefetchContext = undefined,
117
- prefetch_posted_context: PostedGroove.PrefetchContext = undefined,
118
-
119
- open_callback: ?fn (*StateMachine) void = null,
120
- compact_callback: ?fn (*StateMachine) void = null,
121
- checkpoint_callback: ?fn (*StateMachine) void = null,
122
-
123
- tracer_slot: ?tracer.SpanStart,
124
-
125
- pub fn init(allocator: mem.Allocator, grid: *Grid, options: Options) !StateMachine {
126
- var forest = try Forest.init(
127
- allocator,
128
- grid,
129
- options.lsm_forest_node_count,
130
- forest_options(options),
131
- );
132
- errdefer forest.deinit(allocator);
133
-
134
- return StateMachine{
135
- .prepare_timestamp = 0,
136
- .commit_timestamp = 0,
137
- .forest = forest,
138
- .tracer_slot = null,
139
- };
140
- }
141
-
142
- pub fn deinit(self: *StateMachine, allocator: mem.Allocator) void {
143
- assert(self.tracer_slot == null);
144
-
145
- self.forest.deinit(allocator);
146
- }
147
-
148
- pub fn Event(comptime operation: Operation) type {
149
- return switch (operation) {
150
- .create_accounts => Account,
151
- .create_transfers => Transfer,
152
- .lookup_accounts => u128,
153
- .lookup_transfers => u128,
154
- else => unreachable,
155
- };
156
- }
157
-
158
- pub fn Result(comptime operation: Operation) type {
159
- return switch (operation) {
160
- .create_accounts => CreateAccountsResult,
161
- .create_transfers => CreateTransfersResult,
162
- .lookup_accounts => Account,
163
- .lookup_transfers => Transfer,
164
- else => unreachable,
165
- };
166
- }
167
-
168
- pub fn open(self: *StateMachine, callback: fn (*StateMachine) void) void {
169
- assert(self.open_callback == null);
170
- self.open_callback = callback;
171
-
172
- self.forest.open(forest_open_callback);
173
- }
174
-
175
- fn forest_open_callback(forest: *Forest) void {
176
- const self = @fieldParentPtr(StateMachine, "forest", forest);
177
- assert(self.open_callback != null);
178
-
179
- const callback = self.open_callback.?;
180
- self.open_callback = null;
181
- callback(self);
182
- }
183
-
184
- /// Returns the header's timestamp.
185
- pub fn prepare(self: *StateMachine, operation: Operation, input: []u8) u64 {
186
- switch (operation) {
187
- .reserved => unreachable,
188
- .root => unreachable,
189
- .register => {},
190
- .create_accounts => self.prepare_timestamp += mem.bytesAsSlice(Account, input).len,
191
- .create_transfers => self.prepare_timestamp += mem.bytesAsSlice(Transfer, input).len,
192
- .lookup_accounts => {},
193
- .lookup_transfers => {},
194
- }
195
- return self.prepare_timestamp;
196
- }
197
-
198
- pub fn prefetch(
199
- self: *StateMachine,
200
- callback: fn (*StateMachine) void,
201
- op: u64,
202
- operation: Operation,
203
- input: []align(16) const u8,
204
- ) void {
205
- _ = op;
206
- assert(self.prefetch_input == null);
207
- assert(self.prefetch_callback == null);
208
-
209
- // NOTE: prefetch(.register)'s callback should end up calling commit()
210
- // (which is always async) instead of recursing, so this inline callback is fine.
211
- if (operation == .register) {
212
- callback(self);
213
- return;
214
- }
215
-
216
- tracer.start(
217
- &self.tracer_slot,
218
- .main,
219
- .state_machine_prefetch,
220
-
221
- @src(),
222
- );
223
-
224
- self.prefetch_input = input;
225
- self.prefetch_callback = callback;
226
-
227
- // TODO(Snapshots) Pass in the target snapshot.
228
- self.forest.grooves.accounts.prefetch_setup(null);
229
- self.forest.grooves.transfers.prefetch_setup(null);
230
- self.forest.grooves.posted.prefetch_setup(null);
231
-
232
- return switch (operation) {
233
- .reserved, .root, .register => unreachable,
234
- .create_accounts => {
235
- self.prefetch_create_accounts(mem.bytesAsSlice(Account, input));
236
- },
237
- .create_transfers => {
238
- self.prefetch_create_transfers(mem.bytesAsSlice(Transfer, input));
239
- },
240
- .lookup_accounts => {
241
- self.prefetch_lookup_accounts(mem.bytesAsSlice(u128, input));
242
- },
243
- .lookup_transfers => {
244
- self.prefetch_lookup_transfers(mem.bytesAsSlice(u128, input));
245
- },
246
- };
247
- }
248
-
249
- fn prefetch_finish(self: *StateMachine) void {
250
- assert(self.prefetch_input != null);
251
- const callback = self.prefetch_callback.?;
252
- self.prefetch_input = null;
253
- self.prefetch_callback = null;
254
-
255
- tracer.end(
256
- &self.tracer_slot,
257
- .main,
258
- .state_machine_prefetch,
259
- );
260
-
261
- callback(self);
262
- }
263
-
264
- fn prefetch_create_accounts(self: *StateMachine, accounts: []const Account) void {
265
- for (accounts) |*a| {
266
- self.forest.grooves.accounts.prefetch_enqueue(a.id);
267
- }
268
- self.forest.grooves.accounts.prefetch(
269
- prefetch_create_accounts_callback,
270
- &self.prefetch_accounts_context,
271
- );
272
- }
273
-
274
- fn prefetch_create_accounts_callback(completion: *AccountsGroove.PrefetchContext) void {
275
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
276
-
277
- self.prefetch_finish();
278
- }
279
-
280
- fn prefetch_create_transfers(self: *StateMachine, transfers: []const Transfer) void {
281
- for (transfers) |*t| {
282
- self.forest.grooves.transfers.prefetch_enqueue(t.id);
283
-
284
- if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
285
- self.forest.grooves.transfers.prefetch_enqueue(t.pending_id);
286
- // This prefetch isn't run yet, but enqueue it here as well to save an extra
287
- // iteration over transfers.
288
- self.forest.grooves.posted.prefetch_enqueue(t.pending_id);
289
- }
290
- }
291
-
292
- self.forest.grooves.transfers.prefetch(
293
- prefetch_create_transfers_callback_transfers,
294
- &self.prefetch_transfers_context,
295
- );
296
- }
297
-
298
- fn prefetch_create_transfers_callback_transfers(completion: *TransfersGroove.PrefetchContext) void {
299
- const self = @fieldParentPtr(StateMachine, "prefetch_transfers_context", completion);
300
-
301
- const transfers = mem.bytesAsSlice(Event(.create_transfers), self.prefetch_input.?);
302
- for (transfers) |*t| {
303
- if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
304
- 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);
307
- }
308
- } else {
309
- self.forest.grooves.accounts.prefetch_enqueue(t.debit_account_id);
310
- self.forest.grooves.accounts.prefetch_enqueue(t.credit_account_id);
311
- }
312
- }
313
-
314
- self.forest.grooves.accounts.prefetch(
315
- prefetch_create_transfers_callback_accounts,
316
- &self.prefetch_accounts_context,
317
- );
318
- }
319
-
320
- fn prefetch_create_transfers_callback_accounts(completion: *AccountsGroove.PrefetchContext) void {
321
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
322
-
323
- self.forest.grooves.posted.prefetch(
324
- prefetch_create_transfers_callback_posted,
325
- &self.prefetch_posted_context,
326
- );
327
- }
328
-
329
- fn prefetch_create_transfers_callback_posted(completion: *PostedGroove.PrefetchContext) void {
330
- const self = @fieldParentPtr(StateMachine, "prefetch_posted_context", completion);
331
-
332
- self.prefetch_finish();
333
- }
334
-
335
- fn prefetch_lookup_accounts(self: *StateMachine, ids: []const u128) void {
336
- for (ids) |id| {
337
- self.forest.grooves.accounts.prefetch_enqueue(id);
338
- }
339
-
340
- self.forest.grooves.accounts.prefetch(
341
- prefetch_lookup_accounts_callback,
342
- &self.prefetch_accounts_context,
343
- );
344
- }
345
-
346
- fn prefetch_lookup_accounts_callback(completion: *AccountsGroove.PrefetchContext) void {
347
- const self = @fieldParentPtr(StateMachine, "prefetch_accounts_context", completion);
348
-
349
- self.prefetch_finish();
350
- }
351
-
352
- fn prefetch_lookup_transfers(self: *StateMachine, ids: []const u128) void {
353
- for (ids) |id| {
354
- self.forest.grooves.transfers.prefetch_enqueue(id);
355
- }
356
-
357
- self.forest.grooves.transfers.prefetch(
358
- prefetch_lookup_transfers_callback,
359
- &self.prefetch_transfers_context,
360
- );
361
- }
362
-
363
- fn prefetch_lookup_transfers_callback(completion: *TransfersGroove.PrefetchContext) void {
364
- const self = @fieldParentPtr(StateMachine, "prefetch_transfers_context", completion);
365
-
366
- self.prefetch_finish();
367
- }
368
-
369
- pub fn commit(
370
- self: *StateMachine,
371
- client: u128,
372
- op: u64,
373
- timestamp: u64,
374
- operation: Operation,
375
- input: []align(16) const u8,
376
- output: *align(16) [constants.message_body_size_max]u8,
377
- ) usize {
378
- _ = client;
379
- assert(op != 0);
380
- assert(timestamp > self.commit_timestamp);
381
-
382
- tracer.start(
383
- &self.tracer_slot,
384
- .main,
385
- .state_machine_commit,
386
- @src(),
387
- );
388
-
389
- const result = switch (operation) {
390
- .root => unreachable,
391
- .register => 0,
392
- .create_accounts => self.execute(.create_accounts, timestamp, input, output),
393
- .create_transfers => self.execute(.create_transfers, timestamp, input, output),
394
- .lookup_accounts => self.execute_lookup_accounts(input, output),
395
- .lookup_transfers => self.execute_lookup_transfers(input, output),
396
- else => unreachable,
397
- };
398
-
399
- tracer.end(
400
- &self.tracer_slot,
401
- .main,
402
- .state_machine_commit,
403
- );
404
-
405
- return result;
406
- }
407
-
408
- pub fn compact(self: *StateMachine, callback: fn (*StateMachine) void, op: u64) void {
409
- assert(self.compact_callback == null);
410
- assert(self.checkpoint_callback == null);
411
-
412
- tracer.start(
413
- &self.tracer_slot,
414
- .main,
415
- .state_machine_compact,
416
- @src(),
417
- );
418
-
419
- self.compact_callback = callback;
420
- self.forest.compact(compact_finish, op);
421
- }
422
-
423
- fn compact_finish(forest: *Forest) void {
424
- const self = @fieldParentPtr(StateMachine, "forest", forest);
425
- const callback = self.compact_callback.?;
426
- self.compact_callback = null;
427
-
428
- tracer.end(
429
- &self.tracer_slot,
430
- .main,
431
- .state_machine_compact,
432
- );
433
-
434
- callback(self);
435
- }
436
-
437
- pub fn checkpoint(self: *StateMachine, callback: fn (*StateMachine) void) void {
438
- assert(self.compact_callback == null);
439
- assert(self.checkpoint_callback == null);
440
-
441
- self.checkpoint_callback = callback;
442
- self.forest.checkpoint(checkpoint_finish);
443
- }
444
-
445
- fn checkpoint_finish(forest: *Forest) void {
446
- const self = @fieldParentPtr(StateMachine, "forest", forest);
447
- const callback = self.checkpoint_callback.?;
448
- self.checkpoint_callback = null;
449
- callback(self);
450
- }
451
-
452
- fn execute(
453
- self: *StateMachine,
454
- comptime operation: Operation,
455
- timestamp: u64,
456
- input: []align(16) const u8,
457
- output: *align(16) [constants.message_body_size_max]u8,
458
- ) usize {
459
- comptime assert(operation != .lookup_accounts and operation != .lookup_transfers);
460
-
461
- const events = mem.bytesAsSlice(Event(operation), input);
462
- var results = mem.bytesAsSlice(Result(operation), output);
463
- var count: usize = 0;
464
-
465
- var chain: ?usize = null;
466
- var chain_broken = false;
467
-
468
- for (events) |*event_, index| {
469
- var event = event_.*;
470
-
471
- const result = blk: {
472
- if (event.flags.linked) {
473
- if (chain == null) {
474
- chain = index;
475
- assert(chain_broken == false);
476
- }
477
-
478
- if (index == events.len - 1) break :blk .linked_event_chain_open;
479
- }
480
-
481
- if (chain_broken) break :blk .linked_event_failed;
482
- if (event.timestamp != 0) break :blk .timestamp_must_be_zero;
483
-
484
- event.timestamp = timestamp - events.len + index + 1;
485
-
486
- break :blk switch (operation) {
487
- .create_accounts => self.create_account(&event),
488
- .create_transfers => self.create_transfer(&event),
489
- else => unreachable,
490
- };
491
- };
492
- log.debug("{s} {}/{}: {}: {}", .{
493
- @tagName(operation),
494
- index + 1,
495
- events.len,
496
- result,
497
- event,
498
- });
499
- if (result != .ok) {
500
- if (chain) |chain_start_index| {
501
- if (!chain_broken) {
502
- chain_broken = true;
503
- // Rollback events in LIFO order, excluding this event that broke the chain:
504
- self.rollback(operation, input, chain_start_index, index);
505
- // Add errors for rolled back events in FIFO order:
506
- var chain_index = chain_start_index;
507
- while (chain_index < index) : (chain_index += 1) {
508
- results[count] = .{
509
- .index = @intCast(u32, chain_index),
510
- .result = .linked_event_failed,
511
- };
512
- count += 1;
513
- }
514
- } else {
515
- assert(result == .linked_event_failed or result == .linked_event_chain_open);
516
- }
517
- }
518
- results[count] = .{ .index = @intCast(u32, index), .result = result };
519
- count += 1;
520
- }
521
- if (chain != null and (!event.flags.linked or result == .linked_event_chain_open)) {
522
- chain = null;
523
- chain_broken = false;
524
- }
525
- }
526
- assert(chain == null);
527
- assert(chain_broken == false);
528
-
529
- return @sizeOf(Result(operation)) * count;
530
- }
531
-
532
- fn rollback(
533
- self: *StateMachine,
534
- comptime operation: Operation,
535
- input: []const u8,
536
- chain_start_index: usize,
537
- chain_error_index: usize,
538
- ) void {
539
- const events = mem.bytesAsSlice(Event(operation), input);
540
-
541
- // We commit events in FIFO order.
542
- // We must therefore rollback events in LIFO order with a reverse loop.
543
- // We do not rollback `self.commit_timestamp` to ensure that subsequent events are
544
- // timestamped correctly.
545
- var index = chain_error_index;
546
- while (index > chain_start_index) {
547
- index -= 1;
548
-
549
- assert(index >= chain_start_index);
550
- assert(index < chain_error_index);
551
- const event = events[index];
552
- assert(event.timestamp <= self.commit_timestamp);
553
-
554
- switch (operation) {
555
- .create_accounts => self.create_account_rollback(&event),
556
- .create_transfers => self.create_transfer_rollback(&event),
557
- else => unreachable,
558
- }
559
- log.debug("{s} {}/{}: rollback(): {}", .{
560
- @tagName(operation),
561
- index + 1,
562
- events.len,
563
- event,
564
- });
565
- }
566
- assert(index == chain_start_index);
567
- }
568
-
569
- // Accounts that do not fit in the response are omitted.
570
- fn execute_lookup_accounts(
571
- self: *StateMachine,
572
- input: []const u8,
573
- output: *align(16) [constants.message_body_size_max]u8,
574
- ) usize {
575
- const batch = mem.bytesAsSlice(u128, input);
576
- const output_len = @divFloor(output.len, @sizeOf(Account)) * @sizeOf(Account);
577
- const results = mem.bytesAsSlice(Account, output[0..output_len]);
578
- var results_count: usize = 0;
579
- for (batch) |id| {
580
- if (self.get_account(id)) |result| {
581
- results[results_count] = result.*;
582
- results_count += 1;
583
- }
584
- }
585
- return results_count * @sizeOf(Account);
586
- }
587
-
588
- // Transfers that do not fit in the response are omitted.
589
- fn execute_lookup_transfers(
590
- self: *StateMachine,
591
- input: []const u8,
592
- output: *align(16) [constants.message_body_size_max]u8,
593
- ) usize {
594
- const batch = mem.bytesAsSlice(u128, input);
595
- const output_len = @divFloor(output.len, @sizeOf(Transfer)) * @sizeOf(Transfer);
596
- const results = mem.bytesAsSlice(Transfer, output[0..output_len]);
597
- var results_count: usize = 0;
598
- for (batch) |id| {
599
- if (self.get_transfer(id)) |result| {
600
- results[results_count] = result.*;
601
- results_count += 1;
602
- }
603
- }
604
- return results_count * @sizeOf(Transfer);
605
- }
606
-
607
- fn create_account(self: *StateMachine, a: *const Account) CreateAccountResult {
608
- assert(a.timestamp > self.commit_timestamp);
609
-
610
- if (a.flags.padding != 0) return .reserved_flag;
611
- if (!zeroed_48_bytes(a.reserved)) return .reserved_field;
612
-
613
- if (a.id == 0) return .id_must_not_be_zero;
614
- if (a.id == math.maxInt(u128)) return .id_must_not_be_int_max;
615
- if (a.ledger == 0) return .ledger_must_not_be_zero;
616
- if (a.code == 0) return .code_must_not_be_zero;
617
-
618
- if (a.flags.debits_must_not_exceed_credits and a.flags.credits_must_not_exceed_debits) {
619
- return .mutually_exclusive_flags;
620
- }
621
-
622
- if (a.debits_pending != 0) return .debits_pending_must_be_zero;
623
- if (a.debits_posted != 0) return .debits_posted_must_be_zero;
624
- if (a.credits_pending != 0) return .credits_pending_must_be_zero;
625
- if (a.credits_posted != 0) return .credits_posted_must_be_zero;
626
-
627
- if (self.get_account(a.id)) |e| return create_account_exists(a, e);
628
-
629
- self.forest.grooves.accounts.put_no_clobber(a);
630
-
631
- self.commit_timestamp = a.timestamp;
632
- return .ok;
633
- }
634
-
635
- fn create_account_rollback(self: *StateMachine, a: *const Account) void {
636
- self.forest.grooves.accounts.remove(a.id);
637
- }
638
-
639
- fn create_account_exists(a: *const Account, e: *const Account) CreateAccountResult {
640
- assert(a.id == e.id);
641
- if (@bitCast(u16, a.flags) != @bitCast(u16, e.flags)) return .exists_with_different_flags;
642
- 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));
644
- if (a.ledger != e.ledger) return .exists_with_different_ledger;
645
- if (a.code != e.code) return .exists_with_different_code;
646
- return .exists;
647
- }
648
-
649
- fn create_transfer(self: *StateMachine, t: *const Transfer) CreateTransferResult {
650
- assert(t.timestamp > self.commit_timestamp);
651
-
652
- if (t.flags.padding != 0) return .reserved_flag;
653
- if (t.reserved != 0) return .reserved_field;
654
-
655
- if (t.id == 0) return .id_must_not_be_zero;
656
- if (t.id == math.maxInt(u128)) return .id_must_not_be_int_max;
657
-
658
- if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
659
- return self.post_or_void_pending_transfer(t);
660
- }
661
-
662
- if (t.debit_account_id == 0) return .debit_account_id_must_not_be_zero;
663
- if (t.debit_account_id == math.maxInt(u128)) return .debit_account_id_must_not_be_int_max;
664
- if (t.credit_account_id == 0) return .credit_account_id_must_not_be_zero;
665
- if (t.credit_account_id == math.maxInt(u128)) return .credit_account_id_must_not_be_int_max;
666
- if (t.credit_account_id == t.debit_account_id) return .accounts_must_be_different;
667
-
668
- if (t.pending_id != 0) return .pending_id_must_be_zero;
669
- if (!t.flags.pending) {
670
- if (t.timeout != 0) return .timeout_reserved_for_pending_transfer;
671
- }
672
-
673
- if (t.ledger == 0) return .ledger_must_not_be_zero;
674
- if (t.code == 0) return .code_must_not_be_zero;
675
- if (t.amount == 0) return .amount_must_not_be_zero;
676
-
677
- // The etymology of the DR and CR abbreviations for debit/credit is interesting, either:
678
- // 1. derived from the Latin past participles of debitum/creditum, i.e. debere/credere,
679
- // 2. standing for debit record and credit record, or
680
- // 3. relating to debtor and creditor.
681
- // 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);
688
-
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;
691
-
692
- // If the transfer already exists, then it must not influence the overflow or limit checks.
693
- if (self.get_transfer(t.id)) |e| return create_transfer_exists(t, e);
694
-
695
- 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;
698
- }
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;
701
- // 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)) {
703
- return .overflows_debits;
704
- }
705
- if (sum_overflows(t.amount, cr.credits_pending + cr.credits_posted)) {
706
- return .overflows_credits;
707
- }
708
- if (sum_overflows(t.timestamp, t.timeout)) return .overflows_timeout;
709
-
710
- if (dr.debits_exceed_credits(t.amount)) return .exceeds_credits;
711
- if (cr.credits_exceed_debits(t.amount)) return .exceeds_debits;
712
-
713
- self.forest.grooves.transfers.put_no_clobber(t);
714
-
715
- var dr_new = dr.*;
716
- var cr_new = cr.*;
717
- if (t.flags.pending) {
718
- dr_new.debits_pending += t.amount;
719
- cr_new.credits_pending += t.amount;
720
- } else {
721
- dr_new.debits_posted += t.amount;
722
- cr_new.credits_posted += t.amount;
723
- }
724
- self.forest.grooves.accounts.put(&dr_new);
725
- self.forest.grooves.accounts.put(&cr_new);
726
-
727
- self.commit_timestamp = t.timestamp;
728
- return .ok;
729
- }
730
-
731
- fn create_transfer_rollback(self: *StateMachine, t: *const Transfer) void {
732
- if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
733
- return self.post_or_void_pending_transfer_rollback(t);
734
- }
735
-
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);
740
-
741
- if (t.flags.pending) {
742
- dr.debits_pending -= t.amount;
743
- cr.credits_pending -= t.amount;
744
- } else {
745
- dr.debits_posted -= t.amount;
746
- cr.credits_posted -= t.amount;
747
- }
748
- self.forest.grooves.accounts.put(&dr);
749
- self.forest.grooves.accounts.put(&cr);
750
-
751
- self.forest.grooves.transfers.remove(t.id);
752
- }
753
-
754
- fn create_transfer_exists(t: *const Transfer, e: *const Transfer) CreateTransferResult {
755
- assert(t.id == e.id);
756
- // The flags change the behavior of the remaining comparisons, so compare the flags first.
757
- if (@bitCast(u16, t.flags) != @bitCast(u16, e.flags)) return .exists_with_different_flags;
758
- if (t.debit_account_id != e.debit_account_id) {
759
- return .exists_with_different_debit_account_id;
760
- }
761
- if (t.credit_account_id != e.credit_account_id) {
762
- return .exists_with_different_credit_account_id;
763
- }
764
- if (t.user_data != e.user_data) return .exists_with_different_user_data;
765
- assert(t.reserved == 0 and e.reserved == 0);
766
- assert(t.pending_id == 0 and e.pending_id == 0); // We know that the flags are the same.
767
- if (t.timeout != e.timeout) return .exists_with_different_timeout;
768
- assert(t.ledger == e.ledger); // If the accounts are the same, the ledger must be the same.
769
- if (t.code != e.code) return .exists_with_different_code;
770
- if (t.amount != e.amount) return .exists_with_different_amount;
771
- return .exists;
772
- }
773
-
774
- fn post_or_void_pending_transfer(self: *StateMachine, t: *const Transfer) CreateTransferResult {
775
- assert(t.id != 0);
776
- assert(t.flags.padding == 0);
777
- assert(t.reserved == 0);
778
- assert(t.timestamp > self.commit_timestamp);
779
- assert(t.flags.post_pending_transfer or t.flags.void_pending_transfer);
780
-
781
- if (t.flags.post_pending_transfer and t.flags.void_pending_transfer) {
782
- return .cannot_post_and_void_pending_transfer;
783
- }
784
- if (t.flags.pending) return .pending_transfer_cannot_post_or_void_another;
785
- if (t.timeout != 0) return .timeout_reserved_for_pending_transfer;
786
-
787
- if (t.pending_id == 0) return .pending_id_must_not_be_zero;
788
- if (t.pending_id == math.maxInt(u128)) return .pending_id_must_not_be_int_max;
789
- if (t.pending_id == t.id) return .pending_id_must_be_different;
790
-
791
- const p = self.get_transfer(t.pending_id) orelse return .pending_transfer_not_found;
792
- assert(p.id == t.pending_id);
793
- if (!p.flags.pending) return .pending_transfer_not_pending;
794
-
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);
801
- assert(p.amount > 0);
802
-
803
- if (t.debit_account_id > 0 and t.debit_account_id != p.debit_account_id) {
804
- return .pending_transfer_has_different_debit_account_id;
805
- }
806
- if (t.credit_account_id > 0 and t.credit_account_id != p.credit_account_id) {
807
- return .pending_transfer_has_different_credit_account_id;
808
- }
809
- // The user_data field is allowed to differ across pending and posting/voiding transfers.
810
- if (t.ledger > 0 and t.ledger != p.ledger) return .pending_transfer_has_different_ledger;
811
- if (t.code > 0 and t.code != p.code) return .pending_transfer_has_different_code;
812
-
813
- const amount = if (t.amount > 0) t.amount else p.amount;
814
- if (amount > p.amount) return .exceeds_pending_transfer_amount;
815
-
816
- if (t.flags.void_pending_transfer and amount < p.amount) {
817
- return .pending_transfer_has_different_amount;
818
- }
819
-
820
- if (self.get_transfer(t.id)) |e| return post_or_void_pending_transfer_exists(t, e, p);
821
-
822
- if (self.get_posted(t.pending_id)) |posted| {
823
- if (posted) return .pending_transfer_already_posted;
824
- return .pending_transfer_already_voided;
825
- }
826
-
827
- assert(p.timestamp < t.timestamp);
828
- if (p.timeout > 0) {
829
- if (p.timestamp + p.timeout <= t.timestamp) return .pending_transfer_expired;
830
- }
831
-
832
- self.forest.grooves.transfers.put_no_clobber(&Transfer{
833
- .id = t.id,
834
- .debit_account_id = p.debit_account_id,
835
- .credit_account_id = p.credit_account_id,
836
- .user_data = if (t.user_data > 0) t.user_data else p.user_data,
837
- .reserved = p.reserved,
838
- .ledger = p.ledger,
839
- .code = p.code,
840
- .pending_id = t.pending_id,
841
- .timeout = 0,
842
- .timestamp = t.timestamp,
843
- .flags = t.flags,
844
- .amount = amount,
845
- });
846
-
847
- self.forest.grooves.posted.put_no_clobber(t.pending_id, t.flags.post_pending_transfer);
848
-
849
- var dr_new = dr.*;
850
- var cr_new = cr.*;
851
-
852
- dr_new.debits_pending -= p.amount;
853
- cr_new.credits_pending -= p.amount;
854
-
855
- if (t.flags.post_pending_transfer) {
856
- assert(amount > 0);
857
- assert(amount <= p.amount);
858
- dr_new.debits_posted += amount;
859
- cr_new.credits_posted += amount;
860
- }
861
-
862
- self.forest.grooves.accounts.put(&dr_new);
863
- self.forest.grooves.accounts.put(&cr_new);
864
-
865
- self.commit_timestamp = t.timestamp;
866
- return .ok;
867
- }
868
-
869
- fn post_or_void_pending_transfer_rollback(self: *StateMachine, t: *const Transfer) void {
870
- assert(t.id > 0);
871
- assert(t.flags.post_pending_transfer or t.flags.void_pending_transfer);
872
-
873
- assert(t.pending_id > 0);
874
- const p = self.get_transfer(t.pending_id).?;
875
- assert(p.id == t.pending_id);
876
- assert(p.debit_account_id > 0);
877
- assert(p.credit_account_id > 0);
878
-
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);
883
-
884
- if (t.flags.post_pending_transfer) {
885
- const amount = if (t.amount > 0) t.amount else p.amount;
886
- assert(amount > 0);
887
- assert(amount <= p.amount);
888
- dr.debits_posted -= amount;
889
- cr.credits_posted -= amount;
890
- }
891
- dr.debits_pending += p.amount;
892
- cr.credits_pending += p.amount;
893
-
894
- self.forest.grooves.accounts.put(&dr);
895
- self.forest.grooves.accounts.put(&cr);
896
-
897
- self.forest.grooves.posted.remove(t.pending_id);
898
- self.forest.grooves.transfers.remove(t.id);
899
- }
900
-
901
- fn post_or_void_pending_transfer_exists(
902
- t: *const Transfer,
903
- e: *const Transfer,
904
- p: *const Transfer,
905
- ) CreateTransferResult {
906
- assert(p.flags.pending);
907
- assert(t.pending_id == p.id);
908
- assert(t.id != p.id);
909
- assert(t.id == e.id);
910
-
911
- // Do not assume that `e` is necessarily a posting or voiding transfer.
912
- if (@bitCast(u16, t.flags) != @bitCast(u16, e.flags)) {
913
- return .exists_with_different_flags;
914
- }
915
-
916
- // If `e` posted or voided a different pending transfer, then the accounts will differ.
917
- if (t.pending_id != e.pending_id) return .exists_with_different_pending_id;
918
-
919
- assert(e.flags.post_pending_transfer or e.flags.void_pending_transfer);
920
- assert(e.debit_account_id == p.debit_account_id);
921
- assert(e.credit_account_id == p.credit_account_id);
922
- assert(e.reserved == 0);
923
- assert(e.pending_id == p.id);
924
- assert(e.timeout == 0);
925
- assert(e.ledger == p.ledger);
926
- assert(e.code == p.code);
927
- assert(e.timestamp > p.timestamp);
928
-
929
- assert(t.flags.post_pending_transfer == e.flags.post_pending_transfer);
930
- assert(t.flags.void_pending_transfer == e.flags.void_pending_transfer);
931
- assert(t.debit_account_id == 0 or t.debit_account_id == e.debit_account_id);
932
- assert(t.credit_account_id == 0 or t.credit_account_id == e.credit_account_id);
933
- assert(t.reserved == 0);
934
- assert(t.timeout == 0);
935
- assert(t.ledger == 0 or t.ledger == e.ledger);
936
- assert(t.code == 0 or t.code == e.code);
937
- assert(t.timestamp > e.timestamp);
938
-
939
- if (t.user_data == 0) {
940
- if (e.user_data != p.user_data) return .exists_with_different_user_data;
941
- } else {
942
- if (t.user_data != e.user_data) return .exists_with_different_user_data;
943
- }
944
-
945
- if (t.amount == 0) {
946
- if (e.amount != p.amount) return .exists_with_different_amount;
947
- } else {
948
- if (t.amount != e.amount) return .exists_with_different_amount;
949
- }
950
-
951
- return .exists;
952
- }
953
-
954
- fn get_account(self: *const StateMachine, id: u128) ?*const Account {
955
- return self.forest.grooves.accounts.get(id);
956
- }
957
-
958
- fn get_transfer(self: *const StateMachine, id: u128) ?*const Transfer {
959
- return self.forest.grooves.transfers.get(id);
960
- }
961
-
962
- /// Returns whether a pending transfer, if it exists, has already been posted or voided.
963
- fn get_posted(self: *const StateMachine, pending_id: u128) ?bool {
964
- return self.forest.grooves.posted.get(pending_id);
965
- }
966
-
967
- pub fn forest_options(options: Options) Forest.GroovesOptions {
968
- const batch_accounts_max = @intCast(u32, constants.batch_max.create_accounts);
969
- const batch_transfers_max = @intCast(u32, constants.batch_max.create_transfers);
970
- assert(batch_accounts_max == constants.batch_max.lookup_accounts);
971
- assert(batch_transfers_max == constants.batch_max.lookup_transfers);
972
-
973
- return .{
974
- .accounts = .{
975
- .prefetch_entries_max = std.math.max(
976
- // create_account()/lookup_account() looks up 1 account per item.
977
- batch_accounts_max,
978
- // create_transfer()/post_or_void_pending_transfer() looks up 2
979
- // accounts for every transfer.
980
- 2 * batch_transfers_max,
981
- ),
982
- .tree_options_object = .{
983
- .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
- },
990
- .tree_options_id = .{
991
- .cache_entries_max = options.cache_entries_accounts,
992
- .commit_entries_max = batch_accounts_max,
993
- },
994
- .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
- },
1028
- },
1029
- },
1030
- .transfers = .{
1031
- // *2 to fetch pending and post/void transfer.
1032
- .prefetch_entries_max = 2 * batch_transfers_max,
1033
- .tree_options_object = .{
1034
- .cache_entries_max = options.cache_entries_transfers,
1035
- .commit_entries_max = batch_transfers_max,
1036
- },
1037
- .tree_options_id = .{
1038
- .cache_entries_max = options.cache_entries_transfers,
1039
- .commit_entries_max = batch_transfers_max,
1040
- },
1041
- .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 },
1050
- },
1051
- },
1052
- .posted = .{
1053
- .cache_entries_max = options.cache_entries_posted,
1054
- .prefetch_entries_max = batch_transfers_max,
1055
- .commit_entries_max = batch_transfers_max,
1056
- },
1057
- };
1058
- }
1059
- };
1060
- }
1061
-
1062
- fn sum_overflows(a: u64, b: u64) bool {
1063
- var c: u64 = undefined;
1064
- return @addWithOverflow(u64, a, b, &c);
1065
- }
1066
-
1067
- /// Optimizes for the common case, where the array is zeroed. Completely branchless.
1068
- fn zeroed_32_bytes(a: [32]u8) bool {
1069
- const x = @bitCast([4]u64, a);
1070
- return (x[0] | x[1] | x[2] | x[3]) == 0;
1071
- }
1072
-
1073
- fn zeroed_48_bytes(a: [48]u8) bool {
1074
- const x = @bitCast([6]u64, a);
1075
- return (x[0] | x[1] | x[2] | x[3] | x[4] | x[5]) == 0;
1076
- }
1077
-
1078
- /// Optimizes for the common case, where the arrays are equal. Completely branchless.
1079
- fn equal_32_bytes(a: [32]u8, b: [32]u8) bool {
1080
- const x = @bitCast([4]u64, a);
1081
- const y = @bitCast([4]u64, b);
1082
-
1083
- const c = (x[0] ^ y[0]) | (x[1] ^ y[1]);
1084
- const d = (x[2] ^ y[2]) | (x[3] ^ y[3]);
1085
-
1086
- return (c | d) == 0;
1087
- }
1088
-
1089
- fn equal_48_bytes(a: [48]u8, b: [48]u8) bool {
1090
- const x = @bitCast([6]u64, a);
1091
- const y = @bitCast([6]u64, b);
1092
-
1093
- const c = (x[0] ^ y[0]) | (x[1] ^ y[1]);
1094
- const d = (x[2] ^ y[2]) | (x[3] ^ y[3]);
1095
- const e = (x[4] ^ y[4]) | (x[5] ^ y[5]);
1096
-
1097
- return (c | d | e) == 0;
1098
- }
1099
-
1100
- const testing = std.testing;
1101
- const expect = testing.expect;
1102
- const expectEqual = testing.expectEqual;
1103
- const expectEqualSlices = testing.expectEqualSlices;
1104
-
1105
- test "sum_overflows" {
1106
- try expectEqual(false, sum_overflows(math.maxInt(u64), 0));
1107
- try expectEqual(false, sum_overflows(math.maxInt(u64) - 1, 1));
1108
- try expectEqual(false, sum_overflows(1, math.maxInt(u64) - 1));
1109
-
1110
- try expectEqual(true, sum_overflows(math.maxInt(u64), 1));
1111
- try expectEqual(true, sum_overflows(1, math.maxInt(u64)));
1112
-
1113
- try expectEqual(true, sum_overflows(math.maxInt(u64), math.maxInt(u64)));
1114
- try expectEqual(true, sum_overflows(math.maxInt(u64), math.maxInt(u64)));
1115
- }
1116
-
1117
- const TestContext = struct {
1118
- const Storage = @import("testing/storage.zig").Storage;
1119
- const MessagePool = @import("message_pool.zig").MessagePool;
1120
- const data_file_size_min = @import("vsr/superblock.zig").data_file_size_min;
1121
- const SuperBlock = @import("vsr/superblock.zig").SuperBlockType(Storage);
1122
- const Grid = @import("lsm/grid.zig").GridType(Storage);
1123
- const StateMachine = StateMachineType(Storage, .{
1124
- // Overestimate the batch size (in order to overprovision commit_entries_max)
1125
- // because the test never compacts.
1126
- .message_body_size_max = message_body_size_max,
1127
- });
1128
- const message_body_size_max = 32 * @sizeOf(Account);
1129
-
1130
- storage: Storage,
1131
- message_pool: MessagePool,
1132
- superblock: SuperBlock,
1133
- grid: Grid,
1134
- state_machine: StateMachine,
1135
-
1136
- fn init(ctx: *TestContext, allocator: mem.Allocator) !void {
1137
- ctx.storage = try Storage.init(
1138
- allocator,
1139
- 4096,
1140
- .{
1141
- .read_latency_min = 0,
1142
- .read_latency_mean = 0,
1143
- .write_latency_min = 0,
1144
- .write_latency_mean = 0,
1145
- },
1146
- );
1147
- errdefer ctx.storage.deinit(allocator);
1148
-
1149
- ctx.message_pool = .{
1150
- .free_list = null,
1151
- .messages_max = 0,
1152
- };
1153
- errdefer ctx.message_pool.deinit(allocator);
1154
-
1155
- ctx.superblock = try SuperBlock.init(allocator, .{
1156
- .storage = &ctx.storage,
1157
- .storage_size_limit = data_file_size_min,
1158
- .message_pool = &ctx.message_pool,
1159
- });
1160
- errdefer ctx.superblock.deinit(allocator);
1161
-
1162
- // Pretend that the superblock is open so that the Forest can initialize.
1163
- ctx.superblock.opened = true;
1164
- ctx.superblock.working.vsr_state.commit_min = 0;
1165
-
1166
- ctx.grid = try Grid.init(allocator, &ctx.superblock);
1167
- errdefer ctx.grid.deinit(allocator);
1168
-
1169
- ctx.state_machine = try StateMachine.init(allocator, &ctx.grid, .{
1170
- .lsm_forest_node_count = 1,
1171
- .cache_entries_accounts = 2048,
1172
- .cache_entries_transfers = 2048,
1173
- .cache_entries_posted = 2048,
1174
- });
1175
- errdefer ctx.state_machine.deinit(allocator);
1176
- }
1177
-
1178
- fn deinit(ctx: *TestContext, allocator: mem.Allocator) void {
1179
- ctx.storage.deinit(allocator);
1180
- ctx.superblock.deinit(allocator);
1181
- ctx.grid.deinit(allocator);
1182
- ctx.state_machine.deinit(allocator);
1183
- ctx.message_pool.deinit(allocator);
1184
- ctx.* = undefined;
1185
- }
1186
- };
1187
-
1188
- const TestAction = union(enum) {
1189
- // Set the account's balance.
1190
- setup: struct {
1191
- account: u128,
1192
- debits_pending: u64,
1193
- debits_posted: u64,
1194
- credits_pending: u64,
1195
- credits_posted: u64,
1196
- },
1197
-
1198
- commit: TestContext.StateMachine.Operation,
1199
- account: TestCreateAccount,
1200
- transfer: TestCreateTransfer,
1201
-
1202
- lookup_account: struct {
1203
- id: u128,
1204
- balance: ?struct {
1205
- debits_pending: u64,
1206
- debits_posted: u64,
1207
- credits_pending: u64,
1208
- credits_posted: u64,
1209
- } = null,
1210
- },
1211
- lookup_transfer: struct { id: u128, exists: bool },
1212
- };
1213
-
1214
- const TestCreateAccount = struct {
1215
- id: u128,
1216
- user_data: u128 = 0,
1217
- reserved: enum { @"0", @"1" } = .@"0",
1218
- ledger: u32,
1219
- code: u16,
1220
- flags_linked: ?enum { LNK } = null,
1221
- flags_debits_must_not_exceed_credits: ?enum { @"D<C" } = null,
1222
- flags_credits_must_not_exceed_debits: ?enum { @"C<D" } = null,
1223
- flags_padding: u13 = 0,
1224
- debits_pending: u64 = 0,
1225
- debits_posted: u64 = 0,
1226
- credits_pending: u64 = 0,
1227
- credits_posted: u64 = 0,
1228
- timestamp: u64 = 0,
1229
- result: CreateAccountResult,
1230
-
1231
- fn event(a: TestCreateAccount) Account {
1232
- return .{
1233
- .id = a.id,
1234
- .user_data = a.user_data,
1235
- .reserved = switch (a.reserved) {
1236
- .@"0" => [_]u8{0} ** 48,
1237
- .@"1" => [_]u8{1} ** 48,
1238
- },
1239
- .ledger = a.ledger,
1240
- .code = a.code,
1241
- .flags = .{
1242
- .linked = a.flags_linked != null,
1243
- .debits_must_not_exceed_credits = a.flags_debits_must_not_exceed_credits != null,
1244
- .credits_must_not_exceed_debits = a.flags_credits_must_not_exceed_debits != null,
1245
- .padding = a.flags_padding,
1246
- },
1247
- .debits_pending = a.debits_pending,
1248
- .debits_posted = a.debits_posted,
1249
- .credits_pending = a.credits_pending,
1250
- .credits_posted = a.credits_posted,
1251
- .timestamp = a.timestamp,
1252
- };
1253
- }
1254
- };
1255
-
1256
- const TestCreateTransfer = struct {
1257
- id: u128,
1258
- debit_account_id: u128,
1259
- credit_account_id: u128,
1260
- user_data: u128 = 0,
1261
- reserved: u128 = 0,
1262
- pending_id: u128 = 0,
1263
- timeout: u64 = 0,
1264
- ledger: u32,
1265
- code: u16,
1266
- flags_linked: ?enum { LNK } = null,
1267
- flags_pending: ?enum { PEN } = null,
1268
- flags_post_pending_transfer: ?enum { POS } = null,
1269
- flags_void_pending_transfer: ?enum { VOI } = null,
1270
- flags_padding: u12 = 0,
1271
- amount: u64 = 0,
1272
- timestamp: u64 = 0,
1273
- result: CreateTransferResult,
1274
-
1275
- fn event(t: TestCreateTransfer) Transfer {
1276
- return .{
1277
- .id = t.id,
1278
- .debit_account_id = t.debit_account_id,
1279
- .credit_account_id = t.credit_account_id,
1280
- .user_data = t.user_data,
1281
- .reserved = t.reserved,
1282
- .pending_id = t.pending_id,
1283
- .timeout = t.timeout,
1284
- .ledger = t.ledger,
1285
- .code = t.code,
1286
- .flags = .{
1287
- .linked = t.flags_linked != null,
1288
- .pending = t.flags_pending != null,
1289
- .post_pending_transfer = t.flags_post_pending_transfer != null,
1290
- .void_pending_transfer = t.flags_void_pending_transfer != null,
1291
- .padding = t.flags_padding,
1292
- },
1293
- .amount = t.amount,
1294
- .timestamp = t.timestamp,
1295
- };
1296
- }
1297
- };
1298
-
1299
- fn check(comptime test_table: []const u8) !void {
1300
- // TODO(Zig): Disabled because of spurious failure (segfault) on MacOS at the
1301
- // `test_actions.constSlice()`. Most likely a comptime bug that will be resolved by 0.10.
1302
- if (@import("builtin").os.tag == .macos) return;
1303
-
1304
- const parse_table = @import("testing/table.zig").parse;
1305
- const allocator = std.testing.allocator;
1306
-
1307
- var context: TestContext = undefined;
1308
- try context.init(allocator);
1309
- defer context.deinit(allocator);
1310
-
1311
- var accounts = std.AutoHashMap(u128, Account).init(allocator);
1312
- defer accounts.deinit();
1313
-
1314
- var transfers = std.AutoHashMap(u128, Transfer).init(allocator);
1315
- defer transfers.deinit();
1316
-
1317
- var request = std.ArrayListAligned(u8, TestContext.message_body_size_max).init(allocator);
1318
- defer request.deinit();
1319
-
1320
- var reply = std.ArrayListAligned(u8, TestContext.message_body_size_max).init(allocator);
1321
- defer reply.deinit();
1322
-
1323
- var operation: ?TestContext.StateMachine.Operation = null;
1324
-
1325
- const test_actions = parse_table(TestAction, test_table);
1326
- for (test_actions.constSlice()) |test_action| {
1327
- switch (test_action) {
1328
- .setup => |b| {
1329
- assert(operation == null);
1330
-
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);
1337
- },
1338
-
1339
- .account => |a| {
1340
- assert(operation == null or operation.? == .create_accounts);
1341
- operation = .create_accounts;
1342
-
1343
- const event = a.event();
1344
- try request.appendSlice(std.mem.asBytes(&event));
1345
- if (a.result == .ok) {
1346
- try accounts.put(a.id, event);
1347
- } else {
1348
- const result = CreateAccountsResult{
1349
- .index = @intCast(u32, @divExact(request.items.len, @sizeOf(Account)) - 1),
1350
- .result = a.result,
1351
- };
1352
- try reply.appendSlice(std.mem.asBytes(&result));
1353
- }
1354
- },
1355
- .transfer => |t| {
1356
- assert(operation == null or operation.? == .create_transfers);
1357
- operation = .create_transfers;
1358
-
1359
- const event = t.event();
1360
- try request.appendSlice(std.mem.asBytes(&event));
1361
- if (t.result == .ok) {
1362
- try transfers.put(t.id, event);
1363
- } else {
1364
- const result = CreateTransfersResult{
1365
- .index = @intCast(u32, @divExact(request.items.len, @sizeOf(Transfer)) - 1),
1366
- .result = t.result,
1367
- };
1368
- try reply.appendSlice(std.mem.asBytes(&result));
1369
- }
1370
- },
1371
- .lookup_account => |a| {
1372
- assert(operation == null or operation.? == .lookup_accounts);
1373
- operation = .lookup_accounts;
1374
-
1375
- try request.appendSlice(std.mem.asBytes(&a.id));
1376
- if (a.balance) |b| {
1377
- var account = accounts.get(a.id).?;
1378
- account.debits_pending = b.debits_pending;
1379
- account.debits_posted = b.debits_posted;
1380
- account.credits_pending = b.credits_pending;
1381
- account.credits_posted = b.credits_posted;
1382
- try reply.appendSlice(std.mem.asBytes(&account));
1383
- }
1384
- },
1385
- .lookup_transfer => |t| {
1386
- assert(operation == null or operation.? == .lookup_transfers);
1387
- operation = .lookup_transfers;
1388
-
1389
- try request.appendSlice(std.mem.asBytes(&t.id));
1390
- if (t.exists) {
1391
- var transfer = transfers.get(t.id).?;
1392
- try reply.appendSlice(std.mem.asBytes(&transfer));
1393
- }
1394
- },
1395
-
1396
- .commit => |commit_operation| {
1397
- assert(operation == null or operation.? == commit_operation);
1398
-
1399
- context.state_machine.prepare_timestamp += 1;
1400
- const timestamp = context.state_machine.prepare(commit_operation, request.items);
1401
-
1402
- const reply_actual_buffer = try allocator.alignedAlloc(u8, 16, 4096);
1403
- defer allocator.free(reply_actual_buffer);
1404
-
1405
- const reply_actual_size = context.state_machine.commit(
1406
- 0,
1407
- 1,
1408
- timestamp,
1409
- commit_operation,
1410
- request.items,
1411
- reply_actual_buffer[0..TestContext.message_body_size_max],
1412
- );
1413
- var reply_actual = reply_actual_buffer[0..reply_actual_size];
1414
-
1415
- if (commit_operation == .lookup_accounts) {
1416
- for (std.mem.bytesAsSlice(Account, reply_actual)) |*a| a.timestamp = 0;
1417
- }
1418
-
1419
- if (commit_operation == .lookup_transfers) {
1420
- for (std.mem.bytesAsSlice(Transfer, reply_actual)) |*t| t.timestamp = 0;
1421
- }
1422
-
1423
- // TODO(Zig): Use inline-switch to cast the replies to []Reply(operation), then
1424
- // change this to a simple "try".
1425
- testing.expectEqualSlices(u8, reply.items, reply_actual) catch |err| {
1426
- print_results("expect", commit_operation, reply.items);
1427
- print_results("actual", commit_operation, reply_actual);
1428
- return err;
1429
- };
1430
-
1431
- request.clearRetainingCapacity();
1432
- reply.clearRetainingCapacity();
1433
- operation = null;
1434
- },
1435
- }
1436
- }
1437
-
1438
- assert(operation == null);
1439
- assert(request.items.len == 0);
1440
- assert(reply.items.len == 0);
1441
- }
1442
-
1443
- fn print_results(
1444
- label: []const u8,
1445
- operation: TestContext.StateMachine.Operation,
1446
- reply: []const u8,
1447
- ) void {
1448
- inline for (.{
1449
- .create_accounts,
1450
- .create_transfers,
1451
- .lookup_accounts,
1452
- .lookup_transfers,
1453
- }) |o| {
1454
- if (o == operation) {
1455
- const Result = TestContext.StateMachine.Result(o);
1456
- const results = std.mem.bytesAsSlice(Result, reply);
1457
- std.debug.print("{s}={any}\n", .{ label, results });
1458
- return;
1459
- }
1460
- }
1461
- unreachable;
1462
- }
1463
-
1464
- test "create_accounts" {
1465
- try check(
1466
- \\ account A1 U2 _ L3 C4 _ _ _ _ 0 0 0 0 _ ok
1467
- \\ account A0 _ 1 L0 C0 _ D<C C<D 1 1 1 1 1 1 timestamp_must_be_zero
1468
- \\ account A0 _ 1 L0 C0 _ D<C C<D 1 1 1 1 1 _ reserved_flag
1469
- \\ account A0 _ 1 L0 C0 _ D<C C<D _ 1 1 1 1 _ reserved_field
1470
- \\ account A0 _ _ L0 C0 _ D<C C<D _ 1 1 1 1 _ id_must_not_be_zero
1471
- \\ account -0 _ _ L0 C0 _ D<C C<D _ 1 1 1 1 _ id_must_not_be_int_max
1472
- \\ account A1 U1 _ L0 C0 _ D<C C<D _ 1 1 1 1 _ ledger_must_not_be_zero
1473
- \\ account A1 U1 _ L9 C0 _ D<C C<D _ 1 1 1 1 _ code_must_not_be_zero
1474
- \\ account A1 U1 _ L9 C9 _ D<C C<D _ -0 -0 -0 -0 _ mutually_exclusive_flags
1475
- \\ account A1 U1 _ L9 C9 _ D<C _ _ 1 1 1 1 _ debits_pending_must_be_zero
1476
- \\ account A1 U1 _ L9 C9 _ D<C _ _ 0 1 1 1 _ debits_posted_must_be_zero
1477
- \\ account A1 U1 _ L9 C9 _ D<C _ _ 0 0 1 1 _ credits_pending_must_be_zero
1478
- \\ account A1 U1 _ L9 C9 _ D<C _ _ 0 0 0 1 _ credits_posted_must_be_zero
1479
- \\ account A1 U1 _ L9 C9 _ D<C _ _ 0 0 0 0 _ exists_with_different_flags
1480
- \\ account A1 U1 _ L9 C9 _ _ C<D _ 0 0 0 0 _ exists_with_different_flags
1481
- \\ account A1 U1 _ L9 C9 _ _ _ _ 0 0 0 0 _ exists_with_different_user_data
1482
- \\ account A1 U2 _ L9 C9 _ _ _ _ 0 0 0 0 _ exists_with_different_ledger
1483
- \\ account A1 U2 _ L3 C9 _ _ _ _ 0 0 0 0 _ exists_with_different_code
1484
- \\ account A1 U2 _ L3 C4 _ _ _ _ 0 0 0 0 _ exists
1485
- \\ commit create_accounts
1486
- \\
1487
- \\ lookup_account -0 _
1488
- \\ lookup_account A0 _
1489
- \\ lookup_account A1 0 0 0 0
1490
- \\ lookup_account A2 _
1491
- \\ commit lookup_accounts
1492
- );
1493
- }
1494
-
1495
- test "create_accounts: empty" {
1496
- try check(
1497
- \\ commit create_transfers
1498
- );
1499
- }
1500
-
1501
- test "linked accounts" {
1502
- try check(
1503
- \\ account A7 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok // An individual event (successful):
1504
-
1505
- // A chain of 4 events (the last event in the chain closes the chain with linked=false):
1506
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed // Commit/rollback.
1507
- \\ account A2 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed // Commit/rollback.
1508
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ exists // Fail with .exists.
1509
- \\ account A3 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ linked_event_failed // Fail without committing.
1510
-
1511
- // An individual event (successful):
1512
- // This does not see any effect from the failed chain above.
1513
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1514
-
1515
- // A chain of 2 events (the first event fails the chain):
1516
- \\ account A1 _ _ L1 C2 LNK _ _ _ 0 0 0 0 _ exists_with_different_flags
1517
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ linked_event_failed
1518
-
1519
- // An individual event (successful):
1520
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1521
-
1522
- // A chain of 2 events (the last event fails the chain):
1523
- \\ account A3 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed
1524
- \\ account A1 _ _ L2 C1 _ _ _ _ 0 0 0 0 _ exists_with_different_ledger
1525
-
1526
- // A chain of 2 events (successful):
1527
- \\ account A3 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ ok
1528
- \\ account A4 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1529
- \\ commit create_accounts
1530
- \\
1531
- \\ lookup_account A7 0 0 0 0
1532
- \\ lookup_account A1 0 0 0 0
1533
- \\ lookup_account A2 0 0 0 0
1534
- \\ lookup_account A3 0 0 0 0
1535
- \\ lookup_account A4 0 0 0 0
1536
- \\ commit lookup_accounts
1537
- );
1538
-
1539
- try check(
1540
- \\ account A7 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok // An individual event (successful):
1541
-
1542
- // A chain of 4 events:
1543
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed // Commit/rollback.
1544
- \\ account A2 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed // Commit/rollback.
1545
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ exists // Fail with .exists.
1546
- \\ account A3 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ linked_event_failed // Fail without committing.
1547
- \\ commit create_accounts
1548
- \\
1549
- \\ lookup_account A7 0 0 0 0
1550
- \\ lookup_account A1 _
1551
- \\ lookup_account A2 _
1552
- \\ lookup_account A3 _
1553
- \\ commit lookup_accounts
1554
- );
1555
-
1556
- // TODO How can we test that events were in fact rolled back in LIFO order?
1557
- // All our rollback handlers appear to be commutative.
1558
- }
1559
-
1560
- test "linked_event_chain_open" {
1561
- try check(
1562
- // A chain of 3 events (the last event in the chain closes the chain with linked=false):
1563
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ ok
1564
- \\ account A2 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ ok
1565
- \\ account A3 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1566
-
1567
- // An open chain of 2 events:
1568
- \\ account A4 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed
1569
- \\ account A5 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_chain_open
1570
- \\ commit create_accounts
1571
- \\
1572
- \\ lookup_account A1 0 0 0 0
1573
- \\ lookup_account A2 0 0 0 0
1574
- \\ lookup_account A3 0 0 0 0
1575
- \\ lookup_account A4 _
1576
- \\ lookup_account A5 _
1577
- \\ commit lookup_accounts
1578
- );
1579
- }
1580
-
1581
- test "linked_event_chain_open for an already failed batch" {
1582
- try check(
1583
- // An individual event (successful):
1584
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1585
-
1586
- // An open chain of 3 events (the second one fails):
1587
- \\ account A2 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_failed
1588
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ exists_with_different_flags
1589
- \\ account A3 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_chain_open
1590
- \\ commit create_accounts
1591
- \\
1592
- \\ lookup_account A1 0 0 0 0
1593
- \\ lookup_account A2 _
1594
- \\ lookup_account A3 _
1595
- \\ commit lookup_accounts
1596
- );
1597
- }
1598
-
1599
- test "linked_event_chain_open for a batch of 1" {
1600
- try check(
1601
- \\ account A1 _ _ L1 C1 LNK _ _ _ 0 0 0 0 _ linked_event_chain_open
1602
- \\ commit create_accounts
1603
- \\
1604
- \\ lookup_account A1 _
1605
- \\ commit lookup_accounts
1606
- );
1607
- }
1608
-
1609
- // The goal is to ensure that:
1610
- // 1. all CreateTransferResult enums are covered, with
1611
- // 2. enums tested in the order that they are defined, for easier auditing of coverage, and that
1612
- // 3. state machine logic cannot be reordered in any way, breaking determinism.
1613
- test "create_transfers/lookup_transfers" {
1614
- try check(
1615
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1616
- \\ account A2 _ _ L2 C2 _ _ _ _ 0 0 0 0 _ ok
1617
- \\ account A3 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1618
- \\ account A4 _ _ L1 C1 _ D<C _ _ 0 0 0 0 _ ok
1619
- \\ account A5 _ _ L1 C1 _ _ C<D _ 0 0 0 0 _ ok
1620
- \\ commit create_accounts
1621
-
1622
- // Set up initial balances.
1623
- \\ setup A1 100 200 0 0
1624
- \\ setup A2 0 0 0 0
1625
- \\ setup A3 0 0 110 210
1626
- \\ setup A4 20 -700 0 -500
1627
- \\ setup A5 0 -1000 10 -1100
1628
-
1629
- // Test errors by descending precedence.
1630
- \\ transfer T0 A0 A0 _ R1 T1 _ L0 C0 _ PEN _ _ P1 0 1 timestamp_must_be_zero
1631
- \\ transfer T0 A0 A0 _ R1 T1 _ L0 C0 _ PEN _ _ P1 0 _ reserved_flag
1632
- \\ transfer T0 A0 A0 _ R1 T1 _ L0 C0 _ PEN _ _ _ 0 _ reserved_field
1633
- \\ transfer T0 A0 A0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ id_must_not_be_zero
1634
- \\ transfer -0 A0 A0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ id_must_not_be_int_max
1635
- \\ transfer T1 A0 A0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ debit_account_id_must_not_be_zero
1636
- \\ transfer T1 -0 A0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ debit_account_id_must_not_be_int_max
1637
- \\ transfer T1 A8 A0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ credit_account_id_must_not_be_zero
1638
- \\ transfer T1 A8 -0 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ credit_account_id_must_not_be_int_max
1639
- \\ transfer T1 A8 A8 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ accounts_must_be_different
1640
- \\ transfer T1 A8 A9 _ _ T1 _ L0 C0 _ PEN _ _ _ 0 _ pending_id_must_be_zero
1641
- \\ transfer T1 A8 A9 _ _ _ -0 L0 C0 _ _ _ _ _ 0 _ timeout_reserved_for_pending_transfer
1642
- \\ transfer T1 A8 A9 _ _ _ -0 L0 C0 _ PEN _ _ _ 0 _ ledger_must_not_be_zero
1643
- \\ transfer T1 A8 A9 _ _ _ -0 L9 C0 _ PEN _ _ _ 0 _ code_must_not_be_zero
1644
- \\ transfer T1 A8 A9 _ _ _ -0 L9 C1 _ PEN _ _ _ 0 _ amount_must_not_be_zero
1645
- \\ transfer T1 A8 A9 _ _ _ -0 L9 C1 _ PEN _ _ _ 9 _ debit_account_not_found
1646
- \\ transfer T1 A1 A9 _ _ _ -0 L9 C1 _ PEN _ _ _ 9 _ credit_account_not_found
1647
- \\ transfer T1 A1 A2 _ _ _ -0 L9 C1 _ PEN _ _ _ 1 _ accounts_must_have_the_same_ledger
1648
- \\ transfer T1 A1 A3 _ _ _ -0 L9 C1 _ PEN _ _ _ 1 _ transfer_must_have_the_same_ledger_as_accounts
1649
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -99 _ overflows_debits_pending // amount = max - A1.debits_pending + 1
1650
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -109 _ overflows_credits_pending // amount = max - A3.credits_pending + 1
1651
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -199 _ overflows_debits_posted // amount = max - A1.debits_posted + 1
1652
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -209 _ overflows_credits_posted // amount = max - A3.credits_posted + 1
1653
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -299 _ overflows_debits // amount = max - A1.debits_pending - A1.debits_posted + 1
1654
- \\ transfer T1 A1 A3 _ _ _ -0 L1 C1 _ PEN _ _ _ -319 _ overflows_credits // amount = max - A3.credits_pending - A3.credits_posted + 1
1655
- \\ transfer T1 A4 A5 _ _ _ -0 L1 C1 _ PEN _ _ _ 199 _ overflows_timeout // amount = A4.credits_posted - A4.debits_pending - A4.debits_posted + 1
1656
- \\ transfer T1 A4 A5 _ _ _ _ L1 C1 _ _ _ _ _ 199 _ exceeds_credits // amount = A4.credits_posted - A4.debits_pending - A4.debits_posted + 1
1657
- \\ transfer T1 A4 A5 _ _ _ _ L1 C1 _ _ _ _ _ 91 _ exceeds_debits // amount = A5.debits_posted - A5.credits_pending - A5.credits_posted + 1
1658
- \\ transfer T1 A1 A3 _ _ _ 999 L1 C1 _ PEN _ _ _ 123 _ ok
1659
-
1660
- // Ensure that idempotence is only checked after validation.
1661
- \\ transfer T1 A1 A3 _ _ _ 999 L2 C1 _ PEN _ _ _ 123 _ transfer_must_have_the_same_ledger_as_accounts
1662
- \\ transfer T1 A1 A3 U1 _ _ _ L1 C2 _ _ _ _ _ -0 _ exists_with_different_flags
1663
- \\ transfer T1 A3 A1 U1 _ _ 999 L1 C2 _ PEN _ _ _ -0 _ exists_with_different_debit_account_id
1664
- \\ transfer T1 A1 A4 U1 _ _ 999 L1 C2 _ PEN _ _ _ -0 _ exists_with_different_credit_account_id
1665
- \\ transfer T1 A1 A3 U1 _ _ 999 L1 C2 _ PEN _ _ _ -0 _ exists_with_different_user_data
1666
- \\ transfer T1 A1 A3 _ _ _ 998 L1 C2 _ PEN _ _ _ -0 _ exists_with_different_timeout
1667
- \\ transfer T1 A1 A3 _ _ _ 999 L1 C2 _ PEN _ _ _ -0 _ exists_with_different_code
1668
- \\ transfer T1 A1 A3 _ _ _ 999 L1 C1 _ PEN _ _ _ -0 _ exists_with_different_amount
1669
- \\ transfer T1 A1 A3 _ _ _ 999 L1 C1 _ PEN _ _ _ 123 _ exists
1670
- \\ transfer T2 A3 A1 _ _ _ _ L1 C2 _ _ _ _ _ 7 _ ok
1671
- \\ transfer T3 A1 A3 _ _ _ _ L1 C2 _ _ _ _ _ 3 _ ok
1672
- \\ commit create_transfers
1673
- \\
1674
- \\ lookup_account A1 223 203 0 7
1675
- \\ lookup_account A3 0 7 233 213
1676
- \\ commit lookup_accounts
1677
- \\
1678
- \\ lookup_transfer T1 true
1679
- \\ lookup_transfer T2 true
1680
- \\ lookup_transfer T3 true
1681
- \\ lookup_transfer -0 false
1682
- \\ commit lookup_transfers
1683
- );
1684
- }
1685
-
1686
- test "create/lookup 2-phase transfers" {
1687
- try check(
1688
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1689
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1690
- \\ commit create_accounts
1691
-
1692
- // First phase.
1693
- \\ transfer T1 A1 A2 _ _ _ _ L1 C1 _ _ _ _ _ 15 _ ok // Not pending!
1694
- \\ transfer T2 A1 A2 _ _ _ 1000 L1 C1 _ PEN _ _ _ 15 _ ok
1695
- \\ transfer T3 A1 A2 _ _ _ 50 L1 C1 _ PEN _ _ _ 15 _ ok
1696
- \\ transfer T4 A1 A2 _ _ _ 1 L1 C1 _ PEN _ _ _ 15 _ ok
1697
- \\ transfer T5 A1 A2 U9 _ _ 50 L1 C1 _ PEN _ _ _ 7 _ ok
1698
- \\ transfer T6 A1 A2 _ _ _ 0 L1 C1 _ PEN _ _ _ 1 _ ok
1699
- \\ commit create_transfers
1700
-
1701
- // Check balances before resolving.
1702
- \\ lookup_account A1 53 15 0 0
1703
- \\ lookup_account A2 0 0 53 15
1704
- \\ commit lookup_accounts
1705
-
1706
- // Second phase.
1707
- \\ transfer T101 A1 A2 U1 _ T2 _ L1 C1 _ _ POS _ _ 13 _ ok
1708
- \\ transfer T101 A8 A9 U2 _ T0 50 L6 C7 _ PEN POS VOI _ 16 1 timestamp_must_be_zero
1709
- \\ transfer T101 A8 A9 U2 _ T0 50 L6 C7 _ PEN POS VOI _ 16 _ cannot_post_and_void_pending_transfer
1710
- \\ transfer T101 A8 A9 U2 _ T0 50 L6 C7 _ PEN _ VOI _ 16 _ pending_transfer_cannot_post_or_void_another
1711
- \\ transfer T101 A8 A9 U2 _ T0 50 L6 C7 _ _ _ VOI _ 16 _ timeout_reserved_for_pending_transfer
1712
- \\ transfer T101 A8 A9 U2 _ T0 _ L6 C7 _ _ _ VOI _ 16 _ pending_id_must_not_be_zero
1713
- \\ transfer T101 A8 A9 U2 _ -0 _ L6 C7 _ _ _ VOI _ 16 _ pending_id_must_not_be_int_max
1714
- \\ transfer T101 A8 A9 U2 _ 101 _ L6 C7 _ _ _ VOI _ 16 _ pending_id_must_be_different
1715
- \\ transfer T101 A8 A9 U2 _ 102 _ L6 C7 _ _ _ VOI _ 16 _ pending_transfer_not_found
1716
- \\ transfer T101 A8 A9 U2 _ T1 _ L6 C7 _ _ _ VOI _ 16 _ pending_transfer_not_pending
1717
- \\ transfer T101 A8 A9 U2 _ T2 _ L6 C7 _ _ _ VOI _ 16 _ pending_transfer_has_different_debit_account_id
1718
- \\ transfer T101 A1 A9 U2 _ T2 _ L6 C7 _ _ _ VOI _ 16 _ pending_transfer_has_different_credit_account_id
1719
- \\ transfer T101 A1 A2 U2 _ T2 _ L6 C7 _ _ _ VOI _ 16 _ pending_transfer_has_different_ledger
1720
- \\ transfer T101 A1 A2 U2 _ T2 _ L1 C7 _ _ _ VOI _ 16 _ pending_transfer_has_different_code
1721
- \\ transfer T101 A1 A2 U2 _ T2 _ L1 C1 _ _ _ VOI _ 16 _ exceeds_pending_transfer_amount
1722
- \\ transfer T101 A1 A2 U2 _ T2 _ L1 C1 _ _ _ VOI _ 14 _ pending_transfer_has_different_amount
1723
- \\ transfer T101 A1 A2 U2 _ T2 _ L1 C1 _ _ _ VOI _ 15 _ exists_with_different_flags
1724
- \\ transfer T101 A1 A2 U2 _ T3 _ L1 C1 _ _ POS _ _ 14 _ exists_with_different_pending_id
1725
- \\ transfer T101 A1 A2 U2 _ T2 _ L1 C1 _ _ POS _ _ 14 _ exists_with_different_user_data
1726
- \\ transfer T101 A1 A2 U0 _ T2 _ L1 C1 _ _ POS _ _ 14 _ exists_with_different_user_data
1727
- \\ transfer T101 A1 A2 U1 _ T2 _ L1 C1 _ _ POS _ _ 14 _ exists_with_different_amount
1728
- \\ transfer T101 A1 A2 U1 _ T2 _ L1 C1 _ _ POS _ _ _ _ exists_with_different_amount
1729
- \\ transfer T101 A1 A2 U1 _ T2 _ L1 C1 _ _ POS _ _ 13 _ exists
1730
- \\ transfer T102 A1 A2 U1 _ T2 _ L1 C1 _ _ POS _ _ 13 _ pending_transfer_already_posted
1731
- \\ transfer T103 A1 A2 U1 _ T3 _ L1 C1 _ _ _ VOI _ 15 _ ok
1732
- \\ transfer T102 A1 A2 U1 _ T3 _ L1 C1 _ _ POS _ _ 13 _ pending_transfer_already_voided
1733
- \\ transfer T102 A1 A2 U1 _ T4 _ L1 C1 _ _ _ VOI _ 15 _ pending_transfer_expired
1734
- \\ transfer T105 A0 A0 U0 _ T5 _ L0 C0 _ _ POS _ _ _ _ ok
1735
- \\ transfer T106 A0 A0 U0 _ T6 _ L1 C1 _ _ POS _ _ 0 _ ok
1736
- \\ commit create_transfers
1737
-
1738
- // Check balances after resolving.
1739
- \\ lookup_account A1 15 36 0 0
1740
- \\ lookup_account A2 0 0 15 36
1741
- \\ commit lookup_accounts
1742
- );
1743
- }
1744
-
1745
- test "create_transfers: empty" {
1746
- try check(
1747
- \\ commit create_transfers
1748
- );
1749
- }
1750
-
1751
- test "create_transfers/lookup_transfers: failed transfer does not exist" {
1752
- try check(
1753
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1754
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1755
- \\ commit create_accounts
1756
- \\
1757
- \\ transfer T1 A1 A2 _ _ _ _ L1 C1 _ _ _ _ _ 15 _ ok
1758
- \\ transfer T2 A1 A2 _ _ _ _ L0 C1 _ _ _ _ _ 15 _ ledger_must_not_be_zero
1759
- \\ commit create_transfers
1760
- \\
1761
- \\ lookup_account A1 0 15 0 0
1762
- \\ lookup_account A2 0 0 0 15
1763
- \\ commit lookup_accounts
1764
- \\
1765
- \\ lookup_transfer T1 true
1766
- \\ lookup_transfer T2 false
1767
- \\ commit lookup_transfers
1768
- );
1769
- }
1770
-
1771
- test "create_transfers: failed linked-chains are undone" {
1772
- try check(
1773
- \\ account A1 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1774
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1775
- \\ commit create_accounts
1776
- \\
1777
- \\ transfer T1 A1 A2 _ _ _ _ L1 C1 LNK _ _ _ _ 15 _ linked_event_failed
1778
- \\ transfer T2 A1 A2 _ _ _ _ L0 C1 _ _ _ _ _ 15 _ ledger_must_not_be_zero
1779
- \\ commit create_transfers
1780
- \\
1781
- \\ transfer T3 A1 A2 _ _ _ 1 L1 C1 LNK PEN _ _ _ 15 _ linked_event_failed
1782
- \\ transfer T4 A1 A2 _ _ _ _ L0 C1 _ _ _ _ _ 15 _ ledger_must_not_be_zero
1783
- \\ commit create_transfers
1784
- \\
1785
- \\ lookup_account A1 0 0 0 0
1786
- \\ lookup_account A2 0 0 0 0
1787
- \\ commit lookup_accounts
1788
- \\
1789
- \\ lookup_transfer T1 false
1790
- \\ lookup_transfer T2 false
1791
- \\ lookup_transfer T3 false
1792
- \\ lookup_transfer T4 false
1793
- \\ commit lookup_transfers
1794
- );
1795
- }
1796
-
1797
- test "create_transfers: failed linked-chains are undone within a commit" {
1798
- try check(
1799
- \\ account A1 _ _ L1 C1 _ D<C _ _ 0 0 0 0 _ ok
1800
- \\ account A2 _ _ L1 C1 _ _ _ _ 0 0 0 0 _ ok
1801
- \\ commit create_accounts
1802
- \\
1803
- \\ setup A1 0 0 0 20
1804
- \\
1805
- \\ transfer T1 A1 A2 _ _ _ _ L1 C1 LNK _ _ _ _ 15 _ linked_event_failed
1806
- \\ transfer T2 A1 A2 _ _ _ _ L0 C1 _ _ _ _ _ 5 _ ledger_must_not_be_zero
1807
- \\ transfer T3 A1 A2 _ _ _ _ L1 C1 _ _ _ _ _ 15 _ ok
1808
- \\ commit create_transfers
1809
- \\
1810
- \\ lookup_account A1 0 15 0 20
1811
- \\ lookup_account A2 0 0 0 15
1812
- \\ commit lookup_accounts
1813
- \\
1814
- \\ lookup_transfer T1 false
1815
- \\ lookup_transfer T2 false
1816
- \\ lookup_transfer T3 true
1817
- \\ commit lookup_transfers
1818
- );
1819
- }
1820
-
1821
- test "zeroed_32_bytes" {
1822
- try test_zeroed_n_bytes(32);
1823
- }
1824
-
1825
- test "zeroed_48_bytes" {
1826
- try test_zeroed_n_bytes(48);
1827
- }
1828
-
1829
- fn test_zeroed_n_bytes(comptime n: usize) !void {
1830
- const routine = switch (n) {
1831
- 32 => zeroed_32_bytes,
1832
- 48 => zeroed_48_bytes,
1833
- else => unreachable,
1834
- };
1835
- var a = [_]u8{0} ** n;
1836
- var i: usize = 0;
1837
- while (i < a.len) : (i += 1) {
1838
- a[i] = 1;
1839
- try expectEqual(false, routine(a));
1840
- a[i] = 0;
1841
- }
1842
- try expectEqual(true, routine(a));
1843
- }
1844
-
1845
- test "equal_32_bytes" {
1846
- try test_equal_n_bytes(32);
1847
- }
1848
-
1849
- test "equal_48_bytes" {
1850
- try test_equal_n_bytes(48);
1851
- }
1852
-
1853
- fn test_equal_n_bytes(comptime n: usize) !void {
1854
- const routine = switch (n) {
1855
- 32 => equal_32_bytes,
1856
- 48 => equal_48_bytes,
1857
- else => unreachable,
1858
- };
1859
- var a = [_]u8{0} ** n;
1860
- var b = [_]u8{0} ** n;
1861
- var i: usize = 0;
1862
- while (i < a.len) : (i += 1) {
1863
- a[i] = 1;
1864
- try expectEqual(false, routine(a, b));
1865
- a[i] = 0;
1866
-
1867
- b[i] = 1;
1868
- try expectEqual(false, routine(a, b));
1869
- b[i] = 0;
1870
- }
1871
- try expectEqual(true, routine(a, b));
1872
- }
1873
-
1874
- test "StateMachine: ref all decls" {
1875
- const Storage = @import("storage.zig").Storage;
1876
- const StateMachine = StateMachineType(Storage, .{
1877
- .message_body_size_max = 1000 * @sizeOf(Account),
1878
- });
1879
-
1880
- std.testing.refAllDecls(StateMachine);
1881
- }