tigerbeetle-node 0.11.13 → 0.12.1

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