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.
- package/README.md +5 -10
- package/dist/bin/aarch64-linux-gnu/client.node +0 -0
- package/dist/bin/aarch64-linux-musl/client.node +0 -0
- package/dist/bin/aarch64-macos/client.node +0 -0
- package/dist/bin/x86_64-linux-gnu/client.node +0 -0
- package/dist/bin/x86_64-linux-musl/client.node +0 -0
- package/dist/bin/x86_64-macos/client.node +0 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -1
- package/package-lock.json +66 -0
- package/package.json +6 -16
- package/src/index.ts +56 -1
- package/src/node.zig +9 -9
- package/dist/.client.node.sha256 +0 -1
- package/scripts/build_lib.sh +0 -61
- package/scripts/download_node_headers.sh +0 -32
- package/src/tigerbeetle/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/benchmark.bat +0 -55
- package/src/tigerbeetle/scripts/scripts/benchmark.sh +0 -66
- package/src/tigerbeetle/scripts/scripts/confirm_image.sh +0 -44
- package/src/tigerbeetle/scripts/scripts/fail_on_diff.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/fuzz_loop.sh +0 -15
- package/src/tigerbeetle/scripts/scripts/fuzz_loop_hash_log.sh +0 -12
- package/src/tigerbeetle/scripts/scripts/fuzz_unique_errors.sh +0 -7
- package/src/tigerbeetle/scripts/scripts/install.bat +0 -7
- package/src/tigerbeetle/scripts/scripts/install.sh +0 -21
- package/src/tigerbeetle/scripts/scripts/install_zig.bat +0 -113
- package/src/tigerbeetle/scripts/scripts/install_zig.sh +0 -90
- package/src/tigerbeetle/scripts/scripts/lint.zig +0 -199
- package/src/tigerbeetle/scripts/scripts/pre-commit.sh +0 -9
- package/src/tigerbeetle/scripts/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/scripts/shellcheck.sh +0 -5
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +0 -10
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +0 -14
- package/src/tigerbeetle/scripts/upgrade_ubuntu_kernel.sh +0 -48
- package/src/tigerbeetle/scripts/validate_docs.sh +0 -23
- package/src/tigerbeetle/scripts/vr_state_enumerate +0 -46
- package/src/tigerbeetle/src/benchmark.zig +0 -336
- package/src/tigerbeetle/src/config.zig +0 -233
- package/src/tigerbeetle/src/constants.zig +0 -428
- package/src/tigerbeetle/src/ewah.zig +0 -286
- package/src/tigerbeetle/src/ewah_benchmark.zig +0 -120
- package/src/tigerbeetle/src/ewah_fuzz.zig +0 -130
- package/src/tigerbeetle/src/fifo.zig +0 -120
- package/src/tigerbeetle/src/io/benchmark.zig +0 -213
- package/src/tigerbeetle/src/io/darwin.zig +0 -814
- package/src/tigerbeetle/src/io/linux.zig +0 -1071
- package/src/tigerbeetle/src/io/test.zig +0 -643
- package/src/tigerbeetle/src/io/windows.zig +0 -1183
- package/src/tigerbeetle/src/io.zig +0 -34
- package/src/tigerbeetle/src/iops.zig +0 -107
- package/src/tigerbeetle/src/lsm/README.md +0 -308
- package/src/tigerbeetle/src/lsm/binary_search.zig +0 -341
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +0 -125
- package/src/tigerbeetle/src/lsm/compaction.zig +0 -603
- package/src/tigerbeetle/src/lsm/composite_key.zig +0 -77
- package/src/tigerbeetle/src/lsm/direction.zig +0 -11
- package/src/tigerbeetle/src/lsm/eytzinger.zig +0 -587
- package/src/tigerbeetle/src/lsm/eytzinger_benchmark.zig +0 -330
- package/src/tigerbeetle/src/lsm/forest.zig +0 -205
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +0 -450
- package/src/tigerbeetle/src/lsm/grid.zig +0 -573
- package/src/tigerbeetle/src/lsm/groove.zig +0 -1036
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +0 -474
- package/src/tigerbeetle/src/lsm/level_iterator.zig +0 -332
- package/src/tigerbeetle/src/lsm/manifest.zig +0 -617
- package/src/tigerbeetle/src/lsm/manifest_level.zig +0 -878
- package/src/tigerbeetle/src/lsm/manifest_log.zig +0 -789
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +0 -691
- package/src/tigerbeetle/src/lsm/merge_iterator.zig +0 -106
- package/src/tigerbeetle/src/lsm/node_pool.zig +0 -235
- package/src/tigerbeetle/src/lsm/posted_groove.zig +0 -381
- package/src/tigerbeetle/src/lsm/segmented_array.zig +0 -1329
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +0 -148
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +0 -9
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +0 -850
- package/src/tigerbeetle/src/lsm/table.zig +0 -1009
- package/src/tigerbeetle/src/lsm/table_immutable.zig +0 -192
- package/src/tigerbeetle/src/lsm/table_iterator.zig +0 -340
- package/src/tigerbeetle/src/lsm/table_mutable.zig +0 -203
- package/src/tigerbeetle/src/lsm/test.zig +0 -439
- package/src/tigerbeetle/src/lsm/tree.zig +0 -1169
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +0 -479
- package/src/tigerbeetle/src/message_bus.zig +0 -1013
- package/src/tigerbeetle/src/message_pool.zig +0 -156
- package/src/tigerbeetle/src/ring_buffer.zig +0 -399
- package/src/tigerbeetle/src/simulator.zig +0 -580
- package/src/tigerbeetle/src/state_machine/auditor.zig +0 -578
- package/src/tigerbeetle/src/state_machine/workload.zig +0 -883
- package/src/tigerbeetle/src/state_machine.zig +0 -2099
- package/src/tigerbeetle/src/static_allocator.zig +0 -65
- package/src/tigerbeetle/src/stdx.zig +0 -171
- package/src/tigerbeetle/src/storage.zig +0 -393
- package/src/tigerbeetle/src/testing/cluster/message_bus.zig +0 -82
- package/src/tigerbeetle/src/testing/cluster/network.zig +0 -237
- package/src/tigerbeetle/src/testing/cluster/state_checker.zig +0 -169
- package/src/tigerbeetle/src/testing/cluster/storage_checker.zig +0 -202
- package/src/tigerbeetle/src/testing/cluster.zig +0 -444
- package/src/tigerbeetle/src/testing/fuzz.zig +0 -140
- package/src/tigerbeetle/src/testing/hash_log.zig +0 -66
- package/src/tigerbeetle/src/testing/id.zig +0 -99
- package/src/tigerbeetle/src/testing/packet_simulator.zig +0 -374
- package/src/tigerbeetle/src/testing/priority_queue.zig +0 -645
- package/src/tigerbeetle/src/testing/reply_sequence.zig +0 -139
- package/src/tigerbeetle/src/testing/state_machine.zig +0 -250
- package/src/tigerbeetle/src/testing/storage.zig +0 -757
- package/src/tigerbeetle/src/testing/table.zig +0 -247
- package/src/tigerbeetle/src/testing/time.zig +0 -84
- package/src/tigerbeetle/src/tigerbeetle.zig +0 -227
- package/src/tigerbeetle/src/time.zig +0 -112
- package/src/tigerbeetle/src/tracer.zig +0 -529
- package/src/tigerbeetle/src/unit_tests.zig +0 -40
- package/src/tigerbeetle/src/vopr.zig +0 -495
- package/src/tigerbeetle/src/vsr/README.md +0 -209
- package/src/tigerbeetle/src/vsr/client.zig +0 -544
- package/src/tigerbeetle/src/vsr/clock.zig +0 -855
- package/src/tigerbeetle/src/vsr/journal.zig +0 -2415
- package/src/tigerbeetle/src/vsr/journal_format_fuzz.zig +0 -111
- package/src/tigerbeetle/src/vsr/marzullo.zig +0 -309
- package/src/tigerbeetle/src/vsr/replica.zig +0 -6616
- package/src/tigerbeetle/src/vsr/replica_format.zig +0 -219
- package/src/tigerbeetle/src/vsr/superblock.zig +0 -1631
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +0 -256
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +0 -929
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +0 -334
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +0 -390
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +0 -615
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +0 -394
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +0 -314
- 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
|
-
}
|