tigerbeetle-node 0.5.2 → 0.8.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 +97 -78
- package/dist/benchmark.js +96 -94
- package/dist/benchmark.js.map +1 -1
- package/dist/index.d.ts +82 -82
- package/dist/index.js +74 -93
- package/dist/index.js.map +1 -1
- package/dist/test.js +134 -111
- package/dist/test.js.map +1 -1
- package/package.json +3 -2
- package/scripts/download_node_headers.sh +3 -1
- package/src/benchmark.ts +114 -118
- package/src/index.ts +102 -111
- package/src/node.zig +55 -63
- package/src/test.ts +146 -125
- package/src/tigerbeetle/scripts/benchmark.bat +46 -0
- package/src/tigerbeetle/scripts/benchmark.sh +5 -0
- package/src/tigerbeetle/scripts/install_zig.bat +109 -109
- package/src/tigerbeetle/scripts/install_zig.sh +8 -4
- package/src/tigerbeetle/scripts/vopr.bat +47 -47
- package/src/tigerbeetle/scripts/vopr.sh +2 -2
- package/src/tigerbeetle/src/benchmark.zig +65 -102
- package/src/tigerbeetle/src/cli.zig +39 -18
- package/src/tigerbeetle/src/config.zig +44 -25
- package/src/tigerbeetle/src/demo.zig +2 -15
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +10 -10
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +5 -3
- package/src/tigerbeetle/src/{demo_04_create_transfers_two_phase_commit.zig → demo_04_create_pending_transfers.zig} +18 -12
- package/src/tigerbeetle/src/demo_05_post_pending_transfers.zig +37 -0
- package/src/tigerbeetle/src/demo_06_void_pending_transfers.zig +24 -0
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +1 -1
- package/src/tigerbeetle/src/io/benchmark.zig +24 -49
- package/src/tigerbeetle/src/io/darwin.zig +175 -44
- package/src/tigerbeetle/src/io/linux.zig +177 -72
- package/src/tigerbeetle/src/io/test.zig +61 -39
- package/src/tigerbeetle/src/io/windows.zig +1161 -0
- package/src/tigerbeetle/src/io.zig +2 -0
- package/src/tigerbeetle/src/main.zig +31 -10
- package/src/tigerbeetle/src/message_bus.zig +49 -61
- package/src/tigerbeetle/src/message_pool.zig +66 -57
- package/src/tigerbeetle/src/ring_buffer.zig +55 -3
- package/src/tigerbeetle/src/simulator.zig +108 -12
- package/src/tigerbeetle/src/state_machine.zig +1813 -816
- package/src/tigerbeetle/src/storage.zig +0 -230
- package/src/tigerbeetle/src/test/cluster.zig +168 -38
- package/src/tigerbeetle/src/test/message_bus.zig +4 -3
- package/src/tigerbeetle/src/test/network.zig +13 -16
- package/src/tigerbeetle/src/test/packet_simulator.zig +14 -1
- package/src/tigerbeetle/src/test/state_checker.zig +6 -3
- package/src/tigerbeetle/src/test/state_machine.zig +8 -7
- package/src/tigerbeetle/src/test/storage.zig +99 -40
- package/src/tigerbeetle/src/tigerbeetle.zig +108 -101
- package/src/tigerbeetle/src/time.zig +58 -11
- package/src/tigerbeetle/src/vsr/client.zig +18 -32
- package/src/tigerbeetle/src/vsr/clock.zig +1 -1
- package/src/tigerbeetle/src/vsr/journal.zig +1388 -464
- package/src/tigerbeetle/src/vsr/replica.zig +1340 -576
- package/src/tigerbeetle/src/vsr.zig +452 -40
- package/src/translate.zig +10 -0
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +0 -23
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +0 -17
- package/src/tigerbeetle/src/format_test.zig +0 -69
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
2
|
const assert = std.debug.assert;
|
|
3
|
+
const math = std.math;
|
|
4
|
+
const mem = std.mem;
|
|
3
5
|
const log = std.log.scoped(.state_machine);
|
|
4
6
|
|
|
5
7
|
const tb = @import("tigerbeetle.zig");
|
|
@@ -10,49 +12,43 @@ const AccountFlags = tb.AccountFlags;
|
|
|
10
12
|
const Transfer = tb.Transfer;
|
|
11
13
|
const TransferFlags = tb.TransferFlags;
|
|
12
14
|
|
|
13
|
-
const Commit = tb.Commit;
|
|
14
|
-
const CommitFlags = tb.CommitFlags;
|
|
15
|
-
|
|
16
15
|
const CreateAccountsResult = tb.CreateAccountsResult;
|
|
17
16
|
const CreateTransfersResult = tb.CreateTransfersResult;
|
|
18
|
-
const CommitTransfersResult = tb.CommitTransfersResult;
|
|
19
17
|
|
|
20
18
|
const CreateAccountResult = tb.CreateAccountResult;
|
|
21
19
|
const CreateTransferResult = tb.CreateTransferResult;
|
|
22
|
-
const CommitTransferResult = tb.CommitTransferResult;
|
|
23
20
|
const LookupAccountResult = tb.LookupAccountResult;
|
|
24
21
|
|
|
25
22
|
const HashMapAccounts = std.AutoHashMap(u128, Account);
|
|
26
23
|
const HashMapTransfers = std.AutoHashMap(u128, Transfer);
|
|
27
|
-
const
|
|
24
|
+
const HashMapPosted = std.AutoHashMap(u128, bool);
|
|
28
25
|
|
|
29
26
|
pub const StateMachine = struct {
|
|
30
27
|
pub const Operation = enum(u8) {
|
|
31
28
|
/// Operations reserved by VR protocol (for all state machines):
|
|
32
29
|
reserved,
|
|
33
|
-
|
|
30
|
+
root,
|
|
34
31
|
register,
|
|
35
32
|
|
|
36
33
|
/// Operations exported by TigerBeetle:
|
|
37
34
|
create_accounts,
|
|
38
35
|
create_transfers,
|
|
39
|
-
commit_transfers,
|
|
40
36
|
lookup_accounts,
|
|
41
37
|
lookup_transfers,
|
|
42
38
|
};
|
|
43
39
|
|
|
44
|
-
allocator:
|
|
40
|
+
allocator: mem.Allocator,
|
|
45
41
|
prepare_timestamp: u64,
|
|
46
42
|
commit_timestamp: u64,
|
|
47
43
|
accounts: HashMapAccounts,
|
|
48
44
|
transfers: HashMapTransfers,
|
|
49
|
-
|
|
45
|
+
posted: HashMapPosted,
|
|
50
46
|
|
|
51
47
|
pub fn init(
|
|
52
|
-
allocator:
|
|
48
|
+
allocator: mem.Allocator,
|
|
53
49
|
accounts_max: usize,
|
|
54
50
|
transfers_max: usize,
|
|
55
|
-
|
|
51
|
+
transfers_pending_max: usize,
|
|
56
52
|
) !StateMachine {
|
|
57
53
|
var accounts = HashMapAccounts.init(allocator);
|
|
58
54
|
errdefer accounts.deinit();
|
|
@@ -62,12 +58,9 @@ pub const StateMachine = struct {
|
|
|
62
58
|
errdefer transfers.deinit();
|
|
63
59
|
try transfers.ensureTotalCapacity(@intCast(u32, transfers_max));
|
|
64
60
|
|
|
65
|
-
var
|
|
66
|
-
errdefer
|
|
67
|
-
try
|
|
68
|
-
|
|
69
|
-
// TODO After recovery, set prepare_timestamp max(wall clock, op timestamp).
|
|
70
|
-
// TODO After recovery, set commit_timestamp max(wall clock, commit timestamp).
|
|
61
|
+
var posted = HashMapPosted.init(allocator);
|
|
62
|
+
errdefer posted.deinit();
|
|
63
|
+
try posted.ensureTotalCapacity(@intCast(u32, transfers_pending_max));
|
|
71
64
|
|
|
72
65
|
return StateMachine{
|
|
73
66
|
.allocator = allocator,
|
|
@@ -75,21 +68,20 @@ pub const StateMachine = struct {
|
|
|
75
68
|
.commit_timestamp = 0,
|
|
76
69
|
.accounts = accounts,
|
|
77
70
|
.transfers = transfers,
|
|
78
|
-
.
|
|
71
|
+
.posted = posted,
|
|
79
72
|
};
|
|
80
73
|
}
|
|
81
74
|
|
|
82
75
|
pub fn deinit(self: *StateMachine) void {
|
|
83
76
|
self.accounts.deinit();
|
|
84
77
|
self.transfers.deinit();
|
|
85
|
-
self.
|
|
78
|
+
self.posted.deinit();
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
pub fn Event(comptime operation: Operation) type {
|
|
89
82
|
return switch (operation) {
|
|
90
83
|
.create_accounts => Account,
|
|
91
84
|
.create_transfers => Transfer,
|
|
92
|
-
.commit_transfers => Commit,
|
|
93
85
|
.lookup_accounts => u128,
|
|
94
86
|
.lookup_transfers => u128,
|
|
95
87
|
else => unreachable,
|
|
@@ -100,42 +92,33 @@ pub const StateMachine = struct {
|
|
|
100
92
|
return switch (operation) {
|
|
101
93
|
.create_accounts => CreateAccountsResult,
|
|
102
94
|
.create_transfers => CreateTransfersResult,
|
|
103
|
-
.commit_transfers => CommitTransfersResult,
|
|
104
95
|
.lookup_accounts => Account,
|
|
105
96
|
.lookup_transfers => Transfer,
|
|
106
97
|
else => unreachable,
|
|
107
98
|
};
|
|
108
99
|
}
|
|
109
100
|
|
|
110
|
-
|
|
101
|
+
/// Returns the header's timestamp.
|
|
102
|
+
pub fn prepare(self: *StateMachine, operation: Operation, input: []u8) u64 {
|
|
111
103
|
switch (operation) {
|
|
112
|
-
.
|
|
104
|
+
.root => unreachable,
|
|
113
105
|
.register => {},
|
|
114
|
-
.create_accounts => self.prepare_timestamps(
|
|
115
|
-
.create_transfers => self.prepare_timestamps(
|
|
116
|
-
.commit_transfers => self.prepare_timestamps(realtime, .commit_transfers, input),
|
|
106
|
+
.create_accounts => self.prepare_timestamps(.create_accounts, input),
|
|
107
|
+
.create_transfers => self.prepare_timestamps(.create_transfers, input),
|
|
117
108
|
.lookup_accounts => {},
|
|
118
109
|
.lookup_transfers => {},
|
|
119
110
|
else => unreachable,
|
|
120
111
|
}
|
|
112
|
+
return self.prepare_timestamp;
|
|
121
113
|
}
|
|
122
114
|
|
|
123
115
|
fn prepare_timestamps(
|
|
124
116
|
self: *StateMachine,
|
|
125
|
-
realtime: i64,
|
|
126
117
|
comptime operation: Operation,
|
|
127
118
|
input: []u8,
|
|
128
119
|
) void {
|
|
129
|
-
// Guard against the wall clock going backwards by taking the max with timestamps issued:
|
|
130
|
-
self.prepare_timestamp = std.math.max(
|
|
131
|
-
// The cluster `commit_timestamp` may be ahead of our `prepare_timestamp` because this
|
|
132
|
-
// may be our first prepare as a recently elected leader:
|
|
133
|
-
std.math.max(self.prepare_timestamp, self.commit_timestamp) + 1,
|
|
134
|
-
@intCast(u64, realtime),
|
|
135
|
-
);
|
|
136
|
-
assert(self.prepare_timestamp > self.commit_timestamp);
|
|
137
120
|
var sum_reserved_timestamps: usize = 0;
|
|
138
|
-
var events =
|
|
121
|
+
var events = mem.bytesAsSlice(Event(operation), input);
|
|
139
122
|
for (events) |*event| {
|
|
140
123
|
sum_reserved_timestamps += event.timestamp;
|
|
141
124
|
self.prepare_timestamp += 1;
|
|
@@ -155,13 +138,11 @@ pub const StateMachine = struct {
|
|
|
155
138
|
output: []u8,
|
|
156
139
|
) usize {
|
|
157
140
|
_ = client;
|
|
158
|
-
|
|
159
141
|
return switch (operation) {
|
|
160
|
-
.
|
|
142
|
+
.root => unreachable,
|
|
161
143
|
.register => 0,
|
|
162
144
|
.create_accounts => self.execute(.create_accounts, input, output),
|
|
163
145
|
.create_transfers => self.execute(.create_transfers, input, output),
|
|
164
|
-
.commit_transfers => self.execute(.commit_transfers, input, output),
|
|
165
146
|
.lookup_accounts => self.execute_lookup_accounts(input, output),
|
|
166
147
|
.lookup_transfers => self.execute_lookup_transfers(input, output),
|
|
167
148
|
else => unreachable,
|
|
@@ -176,14 +157,14 @@ pub const StateMachine = struct {
|
|
|
176
157
|
) usize {
|
|
177
158
|
comptime assert(operation != .lookup_accounts and operation != .lookup_transfers);
|
|
178
159
|
|
|
179
|
-
const events =
|
|
180
|
-
var results =
|
|
160
|
+
const events = mem.bytesAsSlice(Event(operation), input);
|
|
161
|
+
var results = mem.bytesAsSlice(Result(operation), output);
|
|
181
162
|
var count: usize = 0;
|
|
182
163
|
|
|
183
164
|
var chain: ?usize = null;
|
|
184
165
|
var chain_broken = false;
|
|
185
166
|
|
|
186
|
-
for (events)
|
|
167
|
+
for (events) |*event, index| {
|
|
187
168
|
if (event.flags.linked and chain == null) {
|
|
188
169
|
chain = index;
|
|
189
170
|
assert(chain_broken == false);
|
|
@@ -191,7 +172,6 @@ pub const StateMachine = struct {
|
|
|
191
172
|
const result = if (chain_broken) .linked_event_failed else switch (operation) {
|
|
192
173
|
.create_accounts => self.create_account(event),
|
|
193
174
|
.create_transfers => self.create_transfer(event),
|
|
194
|
-
.commit_transfers => self.commit_transfer(event),
|
|
195
175
|
else => unreachable,
|
|
196
176
|
};
|
|
197
177
|
log.debug("{s} {}/{}: {}: {}", .{
|
|
@@ -243,7 +223,7 @@ pub const StateMachine = struct {
|
|
|
243
223
|
chain_start_index: usize,
|
|
244
224
|
chain_error_index: usize,
|
|
245
225
|
) void {
|
|
246
|
-
const events =
|
|
226
|
+
const events = mem.bytesAsSlice(Event(operation), input);
|
|
247
227
|
|
|
248
228
|
// We commit events in FIFO order.
|
|
249
229
|
// We must therefore rollback events in LIFO order with a reverse loop.
|
|
@@ -259,9 +239,8 @@ pub const StateMachine = struct {
|
|
|
259
239
|
assert(event.timestamp <= self.commit_timestamp);
|
|
260
240
|
|
|
261
241
|
switch (operation) {
|
|
262
|
-
.create_accounts => self.create_account_rollback(event),
|
|
263
|
-
.create_transfers => self.create_transfer_rollback(event),
|
|
264
|
-
.commit_transfers => self.commit_transfer_rollback(event),
|
|
242
|
+
.create_accounts => self.create_account_rollback(&event),
|
|
243
|
+
.create_transfers => self.create_transfer_rollback(&event),
|
|
265
244
|
else => unreachable,
|
|
266
245
|
}
|
|
267
246
|
log.debug("{s} {}/{}: rollback(): {}", .{
|
|
@@ -275,9 +254,9 @@ pub const StateMachine = struct {
|
|
|
275
254
|
}
|
|
276
255
|
|
|
277
256
|
fn execute_lookup_accounts(self: *StateMachine, input: []const u8, output: []u8) usize {
|
|
278
|
-
const batch =
|
|
257
|
+
const batch = mem.bytesAsSlice(u128, input);
|
|
279
258
|
const output_len = @divFloor(output.len, @sizeOf(Account)) * @sizeOf(Account);
|
|
280
|
-
const results =
|
|
259
|
+
const results = mem.bytesAsSlice(Account, output[0..output_len]);
|
|
281
260
|
var results_count: usize = 0;
|
|
282
261
|
for (batch) |id| {
|
|
283
262
|
if (self.get_account(id)) |result| {
|
|
@@ -289,9 +268,9 @@ pub const StateMachine = struct {
|
|
|
289
268
|
}
|
|
290
269
|
|
|
291
270
|
fn execute_lookup_transfers(self: *StateMachine, input: []const u8, output: []u8) usize {
|
|
292
|
-
const batch =
|
|
271
|
+
const batch = mem.bytesAsSlice(u128, input);
|
|
293
272
|
const output_len = @divFloor(output.len, @sizeOf(Transfer)) * @sizeOf(Transfer);
|
|
294
|
-
const results =
|
|
273
|
+
const results = mem.bytesAsSlice(Transfer, output[0..output_len]);
|
|
295
274
|
var results_count: usize = 0;
|
|
296
275
|
for (batch) |id| {
|
|
297
276
|
if (self.get_transfer(id)) |result| {
|
|
@@ -302,186 +281,337 @@ pub const StateMachine = struct {
|
|
|
302
281
|
return results_count * @sizeOf(Transfer);
|
|
303
282
|
}
|
|
304
283
|
|
|
305
|
-
fn create_account(self: *StateMachine, a: Account) CreateAccountResult {
|
|
284
|
+
fn create_account(self: *StateMachine, a: *const Account) CreateAccountResult {
|
|
306
285
|
assert(a.timestamp > self.commit_timestamp);
|
|
307
286
|
|
|
287
|
+
if (a.flags.padding != 0) return .reserved_flag;
|
|
308
288
|
if (!zeroed_48_bytes(a.reserved)) return .reserved_field;
|
|
309
|
-
|
|
289
|
+
|
|
290
|
+
if (a.id == 0) return .id_must_not_be_zero;
|
|
291
|
+
if (a.ledger == 0) return .ledger_must_not_be_zero;
|
|
292
|
+
if (a.code == 0) return .code_must_not_be_zero;
|
|
293
|
+
|
|
294
|
+
if (a.flags.debits_must_not_exceed_credits and a.flags.credits_must_not_exceed_debits) {
|
|
295
|
+
return .mutually_exclusive_flags;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (sum_overflows(a.debits_pending, a.debits_posted)) return .overflows_debits;
|
|
299
|
+
if (sum_overflows(a.credits_pending, a.credits_posted)) return .overflows_credits;
|
|
310
300
|
|
|
311
301
|
// Opening balances may never exceed limits:
|
|
312
302
|
if (a.debits_exceed_credits(0)) return .exceeds_credits;
|
|
313
303
|
if (a.credits_exceed_debits(0)) return .exceeds_debits;
|
|
314
304
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return .exists_with_different_flags;
|
|
322
|
-
}
|
|
323
|
-
if (exists.user_data != a.user_data) return .exists_with_different_user_data;
|
|
324
|
-
if (!equal_48_bytes(exists.reserved, a.reserved)) {
|
|
325
|
-
return .exists_with_different_reserved_field;
|
|
326
|
-
}
|
|
327
|
-
return .exists;
|
|
328
|
-
} else {
|
|
329
|
-
insert.value_ptr.* = a;
|
|
330
|
-
self.commit_timestamp = a.timestamp;
|
|
331
|
-
return .ok;
|
|
332
|
-
}
|
|
305
|
+
if (self.get_account(a.id)) |e| return create_account_exists(a, e);
|
|
306
|
+
|
|
307
|
+
self.accounts.putAssumeCapacityNoClobber(a.id, a.*);
|
|
308
|
+
|
|
309
|
+
self.commit_timestamp = a.timestamp;
|
|
310
|
+
return .ok;
|
|
333
311
|
}
|
|
334
312
|
|
|
335
|
-
fn create_account_rollback(self: *StateMachine, a: Account) void {
|
|
313
|
+
fn create_account_rollback(self: *StateMachine, a: *const Account) void {
|
|
336
314
|
assert(self.accounts.remove(a.id));
|
|
337
315
|
}
|
|
338
316
|
|
|
339
|
-
fn
|
|
317
|
+
fn create_account_exists(a: *const Account, e: *const Account) CreateAccountResult {
|
|
318
|
+
assert(a.id == e.id);
|
|
319
|
+
if (@bitCast(u16, a.flags) != @bitCast(u16, e.flags)) return .exists_with_different_flags;
|
|
320
|
+
if (a.user_data != e.user_data) return .exists_with_different_user_data;
|
|
321
|
+
assert(zeroed_48_bytes(a.reserved) and zeroed_48_bytes(e.reserved));
|
|
322
|
+
if (a.ledger != e.ledger) return .exists_with_different_ledger;
|
|
323
|
+
if (a.code != e.code) return .exists_with_different_code;
|
|
324
|
+
if (a.debits_pending != e.debits_pending) return .exists_with_different_debits_pending;
|
|
325
|
+
if (a.debits_posted != e.debits_posted) return .exists_with_different_debits_posted;
|
|
326
|
+
if (a.credits_pending != e.credits_pending) return .exists_with_different_credits_pending;
|
|
327
|
+
if (a.credits_posted != e.credits_posted) return .exists_with_different_credits_posted;
|
|
328
|
+
return .exists;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
fn create_transfer(self: *StateMachine, t: *const Transfer) CreateTransferResult {
|
|
340
332
|
assert(t.timestamp > self.commit_timestamp);
|
|
341
333
|
|
|
342
|
-
if (t.flags.padding != 0) return .
|
|
343
|
-
if (t.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
334
|
+
if (t.flags.padding != 0) return .reserved_flag;
|
|
335
|
+
if (t.reserved != 0) return .reserved_field;
|
|
336
|
+
|
|
337
|
+
if (t.id == 0) return .id_must_not_be_zero;
|
|
338
|
+
|
|
339
|
+
if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
|
|
340
|
+
return self.post_or_void_pending_transfer(t);
|
|
348
341
|
}
|
|
349
|
-
if (!t.flags.condition and !zeroed_32_bytes(t.reserved)) return .reserved_field;
|
|
350
342
|
|
|
351
|
-
if (t.
|
|
343
|
+
if (t.debit_account_id == 0) return .debit_account_id_must_not_be_zero;
|
|
344
|
+
if (t.credit_account_id == 0) return .credit_account_id_must_not_be_zero;
|
|
345
|
+
if (t.credit_account_id == t.debit_account_id) return .accounts_must_be_different;
|
|
352
346
|
|
|
353
|
-
if (t.
|
|
347
|
+
if (t.pending_id != 0) return .pending_id_must_be_zero;
|
|
348
|
+
if (t.flags.pending) {
|
|
349
|
+
// Otherwise, reserved amounts may never be released.
|
|
350
|
+
if (t.timeout == 0) return .pending_transfer_must_timeout;
|
|
351
|
+
} else {
|
|
352
|
+
if (t.timeout != 0) return .timeout_reserved_for_pending_transfer;
|
|
353
|
+
}
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
|
|
355
|
+
if (t.ledger == 0) return .ledger_must_not_be_zero;
|
|
356
|
+
if (t.code == 0) return .code_must_not_be_zero;
|
|
357
|
+
if (t.amount == 0) return .amount_must_not_be_zero;
|
|
358
|
+
|
|
359
|
+
// The etymology of the DR and CR abbreviations for debit/credit is interesting, either:
|
|
360
|
+
// 1. derived from the Latin past participles of debitum/creditum, i.e. debere/credere,
|
|
357
361
|
// 2. standing for debit record and credit record, or
|
|
358
362
|
// 3. relating to debtor and creditor.
|
|
359
363
|
// We use them to distinguish between `cr` (credit account), and `c` (commit).
|
|
360
|
-
|
|
361
|
-
|
|
364
|
+
const dr = self.get_account(t.debit_account_id) orelse return .debit_account_not_found;
|
|
365
|
+
const cr = self.get_account(t.credit_account_id) orelse return .credit_account_not_found;
|
|
366
|
+
assert(dr.id == t.debit_account_id);
|
|
367
|
+
assert(cr.id == t.credit_account_id);
|
|
362
368
|
assert(t.timestamp > dr.timestamp);
|
|
363
369
|
assert(t.timestamp > cr.timestamp);
|
|
364
370
|
|
|
365
|
-
if (dr.
|
|
371
|
+
if (dr.ledger != cr.ledger) return .accounts_must_have_the_same_ledger;
|
|
372
|
+
if (t.ledger != dr.ledger) return .transfer_must_have_the_same_ledger_as_accounts;
|
|
373
|
+
|
|
374
|
+
// If the transfer already exists, then it must not influence the overflow or limit checks.
|
|
375
|
+
if (self.get_transfer(t.id)) |e| return create_transfer_exists(t, e);
|
|
376
|
+
|
|
377
|
+
if (t.flags.pending) {
|
|
378
|
+
if (sum_overflows(t.amount, dr.debits_pending)) return .overflows_debits_pending;
|
|
379
|
+
if (sum_overflows(t.amount, cr.credits_pending)) return .overflows_credits_pending;
|
|
380
|
+
}
|
|
381
|
+
if (sum_overflows(t.amount, dr.debits_posted)) return .overflows_debits_posted;
|
|
382
|
+
if (sum_overflows(t.amount, cr.credits_posted)) return .overflows_credits_posted;
|
|
383
|
+
// We assert that the sum of the pending and posted balances can never overflow:
|
|
384
|
+
if (sum_overflows(t.amount, dr.debits_pending + dr.debits_posted)) {
|
|
385
|
+
return .overflows_debits;
|
|
386
|
+
}
|
|
387
|
+
if (sum_overflows(t.amount, cr.credits_pending + cr.credits_posted)) {
|
|
388
|
+
return .overflows_credits;
|
|
389
|
+
}
|
|
366
390
|
|
|
367
|
-
// TODO We need a lookup before inserting in case transfer exists and would overflow limits.
|
|
368
|
-
// If the transfer exists, then we should rather return .exists as an error.
|
|
369
391
|
if (dr.debits_exceed_credits(t.amount)) return .exceeds_credits;
|
|
370
392
|
if (cr.credits_exceed_debits(t.amount)) return .exceeds_debits;
|
|
371
393
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
} else if (exists.credit_account_id != t.credit_account_id) {
|
|
378
|
-
return .exists_with_different_credit_account_id;
|
|
379
|
-
}
|
|
380
|
-
if (exists.amount != t.amount) return .exists_with_different_amount;
|
|
381
|
-
if (@bitCast(u32, exists.flags) != @bitCast(u32, t.flags)) {
|
|
382
|
-
return .exists_with_different_flags;
|
|
383
|
-
}
|
|
384
|
-
if (exists.user_data != t.user_data) return .exists_with_different_user_data;
|
|
385
|
-
if (!equal_32_bytes(exists.reserved, t.reserved)) {
|
|
386
|
-
return .exists_with_different_reserved_field;
|
|
387
|
-
}
|
|
388
|
-
if (exists.timeout != t.timeout) return .exists_with_different_timeout;
|
|
389
|
-
return .exists;
|
|
394
|
+
self.transfers.putAssumeCapacityNoClobber(t.id, t.*);
|
|
395
|
+
|
|
396
|
+
if (t.flags.pending) {
|
|
397
|
+
dr.debits_pending += t.amount;
|
|
398
|
+
cr.credits_pending += t.amount;
|
|
390
399
|
} else {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
dr.debits_reserved += t.amount;
|
|
394
|
-
cr.credits_reserved += t.amount;
|
|
395
|
-
} else {
|
|
396
|
-
dr.debits_accepted += t.amount;
|
|
397
|
-
cr.credits_accepted += t.amount;
|
|
398
|
-
}
|
|
399
|
-
self.commit_timestamp = t.timestamp;
|
|
400
|
-
return .ok;
|
|
400
|
+
dr.debits_posted += t.amount;
|
|
401
|
+
cr.credits_posted += t.amount;
|
|
401
402
|
}
|
|
403
|
+
|
|
404
|
+
self.commit_timestamp = t.timestamp;
|
|
405
|
+
return .ok;
|
|
402
406
|
}
|
|
403
407
|
|
|
404
|
-
fn create_transfer_rollback(self: *StateMachine, t: Transfer) void {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
408
|
+
fn create_transfer_rollback(self: *StateMachine, t: *const Transfer) void {
|
|
409
|
+
if (t.flags.post_pending_transfer or t.flags.void_pending_transfer) {
|
|
410
|
+
return self.post_or_void_pending_transfer_rollback(t);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const dr = self.get_account(t.debit_account_id).?;
|
|
414
|
+
const cr = self.get_account(t.credit_account_id).?;
|
|
415
|
+
assert(dr.id == t.debit_account_id);
|
|
416
|
+
assert(cr.id == t.credit_account_id);
|
|
417
|
+
|
|
418
|
+
if (t.flags.pending) {
|
|
419
|
+
dr.debits_pending -= t.amount;
|
|
420
|
+
cr.credits_pending -= t.amount;
|
|
410
421
|
} else {
|
|
411
|
-
dr.
|
|
412
|
-
cr.
|
|
422
|
+
dr.debits_posted -= t.amount;
|
|
423
|
+
cr.credits_posted -= t.amount;
|
|
413
424
|
}
|
|
414
425
|
assert(self.transfers.remove(t.id));
|
|
415
426
|
}
|
|
416
427
|
|
|
417
|
-
fn
|
|
418
|
-
assert(
|
|
428
|
+
fn create_transfer_exists(t: *const Transfer, e: *const Transfer) CreateTransferResult {
|
|
429
|
+
assert(t.id == e.id);
|
|
430
|
+
// The flags change the behavior of the remaining comparisons, so compare the flags first.
|
|
431
|
+
if (@bitCast(u16, t.flags) != @bitCast(u16, e.flags)) return .exists_with_different_flags;
|
|
432
|
+
if (t.debit_account_id != e.debit_account_id) {
|
|
433
|
+
return .exists_with_different_debit_account_id;
|
|
434
|
+
}
|
|
435
|
+
if (t.credit_account_id != e.credit_account_id) {
|
|
436
|
+
return .exists_with_different_credit_account_id;
|
|
437
|
+
}
|
|
438
|
+
if (t.user_data != e.user_data) return .exists_with_different_user_data;
|
|
439
|
+
assert(t.reserved == 0 and e.reserved == 0);
|
|
440
|
+
assert(t.pending_id == 0 and e.pending_id == 0); // We know that the flags are the same.
|
|
441
|
+
if (t.timeout != e.timeout) return .exists_with_different_timeout;
|
|
442
|
+
assert(t.ledger == e.ledger); // If the accounts are the same, the ledger must be the same.
|
|
443
|
+
if (t.code != e.code) return .exists_with_different_code;
|
|
444
|
+
if (t.amount != e.amount) return .exists_with_different_amount;
|
|
445
|
+
return .exists;
|
|
446
|
+
}
|
|
419
447
|
|
|
420
|
-
|
|
421
|
-
|
|
448
|
+
fn post_or_void_pending_transfer(self: *StateMachine, t: *const Transfer) CreateTransferResult {
|
|
449
|
+
assert(t.id != 0);
|
|
450
|
+
assert(t.flags.padding == 0);
|
|
451
|
+
assert(t.reserved == 0);
|
|
452
|
+
assert(t.timestamp > self.commit_timestamp);
|
|
453
|
+
assert(t.flags.post_pending_transfer or t.flags.void_pending_transfer);
|
|
422
454
|
|
|
423
|
-
|
|
424
|
-
|
|
455
|
+
if (t.flags.post_pending_transfer and t.flags.void_pending_transfer) {
|
|
456
|
+
return .cannot_post_and_void_pending_transfer;
|
|
457
|
+
}
|
|
458
|
+
if (t.flags.pending) return .pending_transfer_cannot_post_or_void_another;
|
|
459
|
+
if (t.timeout != 0) return .timeout_reserved_for_pending_transfer;
|
|
460
|
+
|
|
461
|
+
if (t.pending_id == 0) return .pending_id_must_not_be_zero;
|
|
462
|
+
if (t.pending_id == t.id) return .pending_id_must_be_different;
|
|
463
|
+
|
|
464
|
+
const p = self.get_transfer(t.pending_id) orelse return .pending_transfer_not_found;
|
|
465
|
+
assert(p.id == t.pending_id);
|
|
466
|
+
if (!p.flags.pending) return .pending_transfer_not_pending;
|
|
467
|
+
|
|
468
|
+
const dr = self.get_account(p.debit_account_id).?;
|
|
469
|
+
const cr = self.get_account(p.credit_account_id).?;
|
|
470
|
+
assert(dr.id == p.debit_account_id);
|
|
471
|
+
assert(cr.id == p.credit_account_id);
|
|
472
|
+
assert(p.timestamp > dr.timestamp);
|
|
473
|
+
assert(p.timestamp > cr.timestamp);
|
|
474
|
+
assert(p.amount > 0);
|
|
475
|
+
|
|
476
|
+
if (t.debit_account_id > 0 and t.debit_account_id != p.debit_account_id) {
|
|
477
|
+
return .pending_transfer_has_different_debit_account_id;
|
|
478
|
+
}
|
|
479
|
+
if (t.credit_account_id > 0 and t.credit_account_id != p.credit_account_id) {
|
|
480
|
+
return .pending_transfer_has_different_credit_account_id;
|
|
481
|
+
}
|
|
482
|
+
// The user_data field is allowed to differ across pending and posting/voiding transfers.
|
|
483
|
+
if (t.ledger > 0 and t.ledger != p.ledger) return .pending_transfer_has_different_ledger;
|
|
484
|
+
if (t.code > 0 and t.code != p.code) return .pending_transfer_has_different_code;
|
|
425
485
|
|
|
426
|
-
if (
|
|
486
|
+
const amount = if (t.amount > 0) t.amount else p.amount;
|
|
487
|
+
if (amount > p.amount) return .exceeds_pending_transfer_amount;
|
|
427
488
|
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
if (exists.flags.reject and !c.flags.reject) return .already_committed_but_rejected;
|
|
431
|
-
return .already_committed;
|
|
489
|
+
if (t.flags.void_pending_transfer and amount < p.amount) {
|
|
490
|
+
return .pending_transfer_has_different_amount;
|
|
432
491
|
}
|
|
433
492
|
|
|
434
|
-
if (t.
|
|
493
|
+
if (self.get_transfer(t.id)) |e| return post_or_void_pending_transfer_exists(t, e, p);
|
|
435
494
|
|
|
436
|
-
if (t.
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
} else if (c.flags.preimage) {
|
|
440
|
-
return .preimage_requires_condition;
|
|
495
|
+
if (self.get_posted(t.pending_id)) |posted| {
|
|
496
|
+
if (posted) return .pending_transfer_already_posted;
|
|
497
|
+
return .pending_transfer_already_voided;
|
|
441
498
|
}
|
|
442
499
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
500
|
+
assert(p.timestamp < t.timestamp);
|
|
501
|
+
assert(p.timeout > 0);
|
|
502
|
+
if (p.timestamp + p.timeout <= t.timestamp) return .pending_transfer_expired;
|
|
503
|
+
|
|
504
|
+
self.transfers.putAssumeCapacityNoClobber(t.id, .{
|
|
505
|
+
.id = t.id,
|
|
506
|
+
.debit_account_id = p.debit_account_id,
|
|
507
|
+
.credit_account_id = p.credit_account_id,
|
|
508
|
+
.user_data = if (t.user_data > 0) t.user_data else p.user_data,
|
|
509
|
+
.reserved = p.reserved,
|
|
510
|
+
.ledger = p.ledger,
|
|
511
|
+
.code = p.code,
|
|
512
|
+
.pending_id = t.pending_id,
|
|
513
|
+
.timeout = t.timeout,
|
|
514
|
+
.timestamp = t.timestamp,
|
|
515
|
+
.flags = t.flags,
|
|
516
|
+
.amount = amount,
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
self.posted.putAssumeCapacityNoClobber(t.pending_id, t.flags.post_pending_transfer);
|
|
520
|
+
|
|
521
|
+
dr.debits_pending -= p.amount;
|
|
522
|
+
cr.credits_pending -= p.amount;
|
|
523
|
+
|
|
524
|
+
if (t.flags.post_pending_transfer) {
|
|
525
|
+
assert(amount > 0);
|
|
526
|
+
assert(amount <= p.amount);
|
|
527
|
+
dr.debits_posted += amount;
|
|
528
|
+
cr.credits_posted += amount;
|
|
529
|
+
}
|
|
447
530
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
531
|
+
self.commit_timestamp = t.timestamp;
|
|
532
|
+
return .ok;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
fn post_or_void_pending_transfer_rollback(self: *StateMachine, t: *const Transfer) void {
|
|
536
|
+
assert(t.id > 0);
|
|
537
|
+
assert(t.flags.post_pending_transfer or t.flags.void_pending_transfer);
|
|
538
|
+
|
|
539
|
+
assert(t.pending_id > 0);
|
|
540
|
+
const p = self.get_transfer(t.pending_id).?;
|
|
541
|
+
assert(p.id == t.pending_id);
|
|
542
|
+
assert(p.debit_account_id > 0);
|
|
543
|
+
assert(p.credit_account_id > 0);
|
|
544
|
+
|
|
545
|
+
const dr = self.get_account(p.debit_account_id).?;
|
|
546
|
+
const cr = self.get_account(p.credit_account_id).?;
|
|
547
|
+
assert(dr.id == p.debit_account_id);
|
|
548
|
+
assert(cr.id == p.credit_account_id);
|
|
549
|
+
|
|
550
|
+
if (t.flags.post_pending_transfer) {
|
|
551
|
+
const amount = if (t.amount > 0) t.amount else p.amount;
|
|
552
|
+
assert(amount > 0);
|
|
553
|
+
assert(amount <= p.amount);
|
|
554
|
+
dr.debits_posted -= amount;
|
|
555
|
+
cr.credits_posted -= amount;
|
|
556
|
+
}
|
|
557
|
+
dr.debits_pending += p.amount;
|
|
558
|
+
cr.credits_pending += p.amount;
|
|
451
559
|
|
|
452
|
-
|
|
453
|
-
assert(
|
|
454
|
-
|
|
560
|
+
assert(self.posted.remove(t.pending_id));
|
|
561
|
+
assert(self.transfers.remove(t.id));
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
fn post_or_void_pending_transfer_exists(
|
|
565
|
+
t: *const Transfer,
|
|
566
|
+
e: *const Transfer,
|
|
567
|
+
p: *const Transfer,
|
|
568
|
+
) CreateTransferResult {
|
|
569
|
+
assert(p.flags.pending);
|
|
570
|
+
assert(t.pending_id == p.id);
|
|
571
|
+
assert(t.id != p.id);
|
|
572
|
+
assert(t.id == e.id);
|
|
573
|
+
|
|
574
|
+
// Do not assume that `e` is necessarily a posting or voiding transfer.
|
|
575
|
+
if (@bitCast(u16, t.flags) != @bitCast(u16, e.flags)) {
|
|
576
|
+
return .exists_with_different_flags;
|
|
577
|
+
}
|
|
455
578
|
|
|
456
|
-
//
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
579
|
+
// If `e` posted or voided a different pending transfer, then the accounts will differ.
|
|
580
|
+
if (t.pending_id != e.pending_id) return .exists_with_different_pending_id;
|
|
581
|
+
|
|
582
|
+
assert(e.flags.post_pending_transfer or e.flags.void_pending_transfer);
|
|
583
|
+
assert(e.debit_account_id == p.debit_account_id);
|
|
584
|
+
assert(e.credit_account_id == p.credit_account_id);
|
|
585
|
+
assert(e.reserved == 0);
|
|
586
|
+
assert(e.pending_id == p.id);
|
|
587
|
+
assert(e.timeout == 0);
|
|
588
|
+
assert(e.ledger == p.ledger);
|
|
589
|
+
assert(e.code == p.code);
|
|
590
|
+
assert(e.timestamp > p.timestamp);
|
|
591
|
+
|
|
592
|
+
assert(t.flags.post_pending_transfer == e.flags.post_pending_transfer);
|
|
593
|
+
assert(t.flags.void_pending_transfer == e.flags.void_pending_transfer);
|
|
594
|
+
assert(t.debit_account_id == 0 or t.debit_account_id == e.debit_account_id);
|
|
595
|
+
assert(t.credit_account_id == 0 or t.credit_account_id == e.credit_account_id);
|
|
596
|
+
assert(t.reserved == 0);
|
|
597
|
+
assert(t.timeout == 0);
|
|
598
|
+
assert(t.ledger == 0 or t.ledger == e.ledger);
|
|
599
|
+
assert(t.code == 0 or t.code == e.code);
|
|
600
|
+
assert(t.timestamp > e.timestamp);
|
|
601
|
+
|
|
602
|
+
if (t.user_data == 0) {
|
|
603
|
+
if (e.user_data != p.user_data) return .exists_with_different_user_data;
|
|
460
604
|
} else {
|
|
461
|
-
|
|
462
|
-
dr.debits_reserved -= t.amount;
|
|
463
|
-
cr.credits_reserved -= t.amount;
|
|
464
|
-
if (!c.flags.reject) {
|
|
465
|
-
dr.debits_accepted += t.amount;
|
|
466
|
-
cr.credits_accepted += t.amount;
|
|
467
|
-
}
|
|
468
|
-
self.commit_timestamp = c.timestamp;
|
|
469
|
-
return .ok;
|
|
605
|
+
if (t.user_data != e.user_data) return .exists_with_different_user_data;
|
|
470
606
|
}
|
|
471
|
-
}
|
|
472
607
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
var cr = self.get_account(t.credit_account_id).?;
|
|
478
|
-
dr.debits_reserved += t.amount;
|
|
479
|
-
cr.credits_reserved += t.amount;
|
|
480
|
-
if (!c.flags.reject) {
|
|
481
|
-
dr.debits_accepted -= t.amount;
|
|
482
|
-
cr.credits_accepted -= t.amount;
|
|
608
|
+
if (t.amount == 0) {
|
|
609
|
+
if (e.amount != p.amount) return .exists_with_different_amount;
|
|
610
|
+
} else {
|
|
611
|
+
if (t.amount != e.amount) return .exists_with_different_amount;
|
|
483
612
|
}
|
|
484
|
-
|
|
613
|
+
|
|
614
|
+
return .exists;
|
|
485
615
|
}
|
|
486
616
|
|
|
487
617
|
/// This is our core private method for changing balances.
|
|
@@ -490,30 +620,24 @@ pub const StateMachine = struct {
|
|
|
490
620
|
/// This pointer is invalidated if the hash map is resized by another insert, e.g. if we get a
|
|
491
621
|
/// pointer, insert another account without capacity, and then modify this pointer... BOOM!
|
|
492
622
|
/// This is a sharp tool but replaces a lookup, copy and update with a single lookup.
|
|
493
|
-
fn get_account(self: *StateMachine, id: u128) ?*Account {
|
|
623
|
+
fn get_account(self: *const StateMachine, id: u128) ?*Account {
|
|
494
624
|
return self.accounts.getPtr(id);
|
|
495
625
|
}
|
|
496
626
|
|
|
497
627
|
/// See the comment for get_account().
|
|
498
|
-
fn get_transfer(self: *StateMachine, id: u128) ?*Transfer {
|
|
628
|
+
fn get_transfer(self: *const StateMachine, id: u128) ?*Transfer {
|
|
499
629
|
return self.transfers.getPtr(id);
|
|
500
630
|
}
|
|
501
631
|
|
|
502
|
-
///
|
|
503
|
-
fn
|
|
504
|
-
return self.
|
|
632
|
+
/// Returns whether a pending transfer, if it exists, has already been posted or voided.
|
|
633
|
+
fn get_posted(self: *const StateMachine, pending_id: u128) ?bool {
|
|
634
|
+
return self.posted.get(pending_id);
|
|
505
635
|
}
|
|
506
636
|
};
|
|
507
637
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
// Then we can simply provide the result bitset to the state machine when committing.
|
|
512
|
-
// This will improve crypto performance significantly by a factor of 8x.
|
|
513
|
-
fn valid_preimage(condition: [32]u8, preimage: [32]u8) bool {
|
|
514
|
-
var target: [32]u8 = undefined;
|
|
515
|
-
std.crypto.hash.sha2.Sha256.hash(&preimage, &target, .{});
|
|
516
|
-
return std.crypto.utils.timingSafeEql([32]u8, target, condition);
|
|
638
|
+
fn sum_overflows(a: u64, b: u64) bool {
|
|
639
|
+
var c: u64 = undefined;
|
|
640
|
+
return @addWithOverflow(u64, a, b, &c);
|
|
517
641
|
}
|
|
518
642
|
|
|
519
643
|
/// Optimizes for the common case, where the array is zeroed. Completely branchless.
|
|
@@ -550,924 +674,1797 @@ fn equal_48_bytes(a: [48]u8, b: [48]u8) bool {
|
|
|
550
674
|
}
|
|
551
675
|
|
|
552
676
|
const testing = std.testing;
|
|
677
|
+
const expect = testing.expect;
|
|
678
|
+
const expectEqual = testing.expectEqual;
|
|
679
|
+
const expectEqualSlices = testing.expectEqualSlices;
|
|
680
|
+
|
|
681
|
+
test "sum_overflows" {
|
|
682
|
+
try expectEqual(false, sum_overflows(math.maxInt(u64), 0));
|
|
683
|
+
try expectEqual(false, sum_overflows(math.maxInt(u64) - 1, 1));
|
|
684
|
+
try expectEqual(false, sum_overflows(1, math.maxInt(u64) - 1));
|
|
553
685
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
defer arena.deinit();
|
|
686
|
+
try expectEqual(true, sum_overflows(math.maxInt(u64), 1));
|
|
687
|
+
try expectEqual(true, sum_overflows(1, math.maxInt(u64)));
|
|
557
688
|
|
|
558
|
-
|
|
689
|
+
try expectEqual(true, sum_overflows(math.maxInt(u64), math.maxInt(u64)));
|
|
690
|
+
try expectEqual(true, sum_overflows(math.maxInt(u64), math.maxInt(u64)));
|
|
691
|
+
}
|
|
559
692
|
|
|
693
|
+
test "create/lookup/rollback accounts" {
|
|
560
694
|
const Vector = struct { result: CreateAccountResult, object: Account };
|
|
561
695
|
|
|
562
696
|
const vectors = [_]Vector{
|
|
563
|
-
|
|
564
|
-
.result = .
|
|
565
|
-
.object =
|
|
697
|
+
.{
|
|
698
|
+
.result = .ok,
|
|
699
|
+
.object = mem.zeroInit(Account, .{
|
|
566
700
|
.id = 1,
|
|
701
|
+
.user_data = 2,
|
|
702
|
+
.ledger = 3,
|
|
703
|
+
.code = 4,
|
|
704
|
+
.debits_pending = 5,
|
|
705
|
+
.debits_posted = 6,
|
|
706
|
+
.credits_pending = 7,
|
|
707
|
+
.credits_posted = 8,
|
|
567
708
|
.timestamp = 1,
|
|
568
|
-
.flags = .{ .padding = 1 },
|
|
569
709
|
}),
|
|
570
710
|
},
|
|
571
|
-
|
|
711
|
+
.{
|
|
712
|
+
.result = .reserved_flag,
|
|
713
|
+
.object = mem.zeroInit(Account, .{
|
|
714
|
+
.id = 0,
|
|
715
|
+
.user_data = 0,
|
|
716
|
+
.reserved = [_]u8{1} ** 48,
|
|
717
|
+
.ledger = 0,
|
|
718
|
+
.code = 0,
|
|
719
|
+
.flags = .{
|
|
720
|
+
.padding = 1,
|
|
721
|
+
.debits_must_not_exceed_credits = true,
|
|
722
|
+
.credits_must_not_exceed_debits = true,
|
|
723
|
+
},
|
|
724
|
+
.debits_pending = math.maxInt(u64),
|
|
725
|
+
.debits_posted = math.maxInt(u64),
|
|
726
|
+
.credits_pending = math.maxInt(u64),
|
|
727
|
+
.credits_posted = math.maxInt(u64),
|
|
728
|
+
.timestamp = 2,
|
|
729
|
+
}),
|
|
730
|
+
},
|
|
731
|
+
.{
|
|
572
732
|
.result = .reserved_field,
|
|
573
|
-
.object =
|
|
574
|
-
.id =
|
|
575
|
-
.
|
|
733
|
+
.object = mem.zeroInit(Account, .{
|
|
734
|
+
.id = 0,
|
|
735
|
+
.user_data = 0,
|
|
576
736
|
.reserved = [_]u8{1} ** 48,
|
|
737
|
+
.ledger = 0,
|
|
738
|
+
.code = 0,
|
|
739
|
+
.flags = .{
|
|
740
|
+
.padding = 0,
|
|
741
|
+
.debits_must_not_exceed_credits = true,
|
|
742
|
+
.credits_must_not_exceed_debits = true,
|
|
743
|
+
},
|
|
744
|
+
.debits_pending = math.maxInt(u64),
|
|
745
|
+
.debits_posted = math.maxInt(u64),
|
|
746
|
+
.credits_pending = math.maxInt(u64),
|
|
747
|
+
.credits_posted = math.maxInt(u64),
|
|
748
|
+
.timestamp = 2,
|
|
577
749
|
}),
|
|
578
750
|
},
|
|
579
|
-
|
|
580
|
-
.result = .
|
|
581
|
-
.object =
|
|
582
|
-
.id =
|
|
583
|
-
.
|
|
584
|
-
.
|
|
585
|
-
.
|
|
751
|
+
.{
|
|
752
|
+
.result = .id_must_not_be_zero,
|
|
753
|
+
.object = mem.zeroInit(Account, .{
|
|
754
|
+
.id = 0,
|
|
755
|
+
.user_data = 0,
|
|
756
|
+
.ledger = 0,
|
|
757
|
+
.code = 0,
|
|
758
|
+
.flags = .{
|
|
759
|
+
.padding = 0,
|
|
760
|
+
.debits_must_not_exceed_credits = true,
|
|
761
|
+
.credits_must_not_exceed_debits = true,
|
|
762
|
+
},
|
|
763
|
+
.debits_pending = math.maxInt(u64),
|
|
764
|
+
.debits_posted = math.maxInt(u64),
|
|
765
|
+
.credits_pending = math.maxInt(u64),
|
|
766
|
+
.credits_posted = math.maxInt(u64),
|
|
767
|
+
.timestamp = 2,
|
|
586
768
|
}),
|
|
587
769
|
},
|
|
588
|
-
|
|
589
|
-
.result = .
|
|
590
|
-
.object =
|
|
591
|
-
.id =
|
|
592
|
-
.
|
|
593
|
-
.
|
|
594
|
-
.
|
|
770
|
+
.{
|
|
771
|
+
.result = .ledger_must_not_be_zero,
|
|
772
|
+
.object = mem.zeroInit(Account, .{
|
|
773
|
+
.id = 1,
|
|
774
|
+
.user_data = 20,
|
|
775
|
+
.ledger = 0,
|
|
776
|
+
.code = 0,
|
|
777
|
+
.flags = .{
|
|
778
|
+
.padding = 0,
|
|
779
|
+
.debits_must_not_exceed_credits = true,
|
|
780
|
+
.credits_must_not_exceed_debits = true,
|
|
781
|
+
},
|
|
782
|
+
.debits_pending = math.maxInt(u64),
|
|
783
|
+
.debits_posted = math.maxInt(u64),
|
|
784
|
+
.credits_pending = math.maxInt(u64),
|
|
785
|
+
.credits_posted = math.maxInt(u64),
|
|
786
|
+
.timestamp = 2,
|
|
595
787
|
}),
|
|
596
788
|
},
|
|
597
|
-
|
|
598
|
-
.result = .
|
|
599
|
-
.object =
|
|
600
|
-
.id =
|
|
601
|
-
.
|
|
602
|
-
.
|
|
603
|
-
.
|
|
789
|
+
.{
|
|
790
|
+
.result = .code_must_not_be_zero,
|
|
791
|
+
.object = mem.zeroInit(Account, .{
|
|
792
|
+
.id = 1,
|
|
793
|
+
.user_data = 20,
|
|
794
|
+
.ledger = 30,
|
|
795
|
+
.code = 0,
|
|
796
|
+
.flags = .{
|
|
797
|
+
.debits_must_not_exceed_credits = true,
|
|
798
|
+
.credits_must_not_exceed_debits = true,
|
|
799
|
+
},
|
|
800
|
+
.debits_pending = math.maxInt(u64),
|
|
801
|
+
.debits_posted = math.maxInt(u64),
|
|
802
|
+
.credits_pending = math.maxInt(u64),
|
|
803
|
+
.credits_posted = math.maxInt(u64),
|
|
804
|
+
.timestamp = 2,
|
|
604
805
|
}),
|
|
605
806
|
},
|
|
606
|
-
|
|
607
|
-
.result = .
|
|
608
|
-
.object =
|
|
609
|
-
.id =
|
|
610
|
-
.
|
|
611
|
-
.
|
|
612
|
-
.
|
|
807
|
+
.{
|
|
808
|
+
.result = .mutually_exclusive_flags,
|
|
809
|
+
.object = mem.zeroInit(Account, .{
|
|
810
|
+
.id = 1,
|
|
811
|
+
.user_data = 20,
|
|
812
|
+
.ledger = 30,
|
|
813
|
+
.code = 40,
|
|
814
|
+
.flags = .{
|
|
815
|
+
.debits_must_not_exceed_credits = true,
|
|
816
|
+
.credits_must_not_exceed_debits = true,
|
|
817
|
+
},
|
|
818
|
+
.debits_pending = math.maxInt(u64),
|
|
819
|
+
.debits_posted = math.maxInt(u64),
|
|
820
|
+
.credits_pending = math.maxInt(u64),
|
|
821
|
+
.credits_posted = math.maxInt(u64),
|
|
822
|
+
.timestamp = 2,
|
|
613
823
|
}),
|
|
614
824
|
},
|
|
615
|
-
|
|
616
|
-
.result = .
|
|
617
|
-
.object =
|
|
618
|
-
.id =
|
|
619
|
-
.
|
|
825
|
+
.{
|
|
826
|
+
.result = .overflows_debits,
|
|
827
|
+
.object = mem.zeroInit(Account, .{
|
|
828
|
+
.id = 1,
|
|
829
|
+
.user_data = 20,
|
|
830
|
+
.ledger = 30,
|
|
831
|
+
.code = 40,
|
|
832
|
+
.flags = .{
|
|
833
|
+
.debits_must_not_exceed_credits = true,
|
|
834
|
+
},
|
|
835
|
+
.debits_pending = math.maxInt(u64),
|
|
836
|
+
.debits_posted = 60,
|
|
837
|
+
.credits_pending = math.maxInt(u64),
|
|
838
|
+
.credits_posted = 80,
|
|
839
|
+
.timestamp = 2,
|
|
620
840
|
}),
|
|
621
841
|
},
|
|
622
|
-
|
|
623
|
-
.result = .
|
|
624
|
-
.object =
|
|
625
|
-
.id =
|
|
842
|
+
.{
|
|
843
|
+
.result = .overflows_credits,
|
|
844
|
+
.object = mem.zeroInit(Account, .{
|
|
845
|
+
.id = 1,
|
|
846
|
+
.user_data = 20,
|
|
847
|
+
.ledger = 30,
|
|
848
|
+
.code = 40,
|
|
849
|
+
.flags = .{
|
|
850
|
+
.credits_must_not_exceed_debits = true,
|
|
851
|
+
},
|
|
852
|
+
.debits_pending = 50,
|
|
853
|
+
.debits_posted = 60,
|
|
854
|
+
.credits_pending = math.maxInt(u64),
|
|
855
|
+
.credits_posted = 80,
|
|
626
856
|
.timestamp = 2,
|
|
627
857
|
}),
|
|
628
858
|
},
|
|
629
|
-
|
|
630
|
-
.result = .
|
|
631
|
-
.object =
|
|
632
|
-
.id =
|
|
859
|
+
.{
|
|
860
|
+
.result = .exceeds_credits,
|
|
861
|
+
.object = mem.zeroInit(Account, .{
|
|
862
|
+
.id = 1,
|
|
863
|
+
.user_data = 20,
|
|
864
|
+
.ledger = 30,
|
|
865
|
+
.code = 40,
|
|
866
|
+
.flags = .{
|
|
867
|
+
.debits_must_not_exceed_credits = true,
|
|
868
|
+
},
|
|
869
|
+
.debits_pending = 50,
|
|
870
|
+
.debits_posted = 60,
|
|
871
|
+
.credits_pending = 1,
|
|
872
|
+
.credits_posted = 109,
|
|
633
873
|
.timestamp = 2,
|
|
634
|
-
.user_data = 'U',
|
|
635
|
-
.unit = 9,
|
|
636
874
|
}),
|
|
637
875
|
},
|
|
638
|
-
|
|
639
|
-
.result = .
|
|
640
|
-
.object =
|
|
641
|
-
.id =
|
|
642
|
-
.
|
|
643
|
-
.
|
|
644
|
-
.
|
|
876
|
+
.{
|
|
877
|
+
.result = .exceeds_debits,
|
|
878
|
+
.object = mem.zeroInit(Account, .{
|
|
879
|
+
.id = 1,
|
|
880
|
+
.user_data = 20,
|
|
881
|
+
.ledger = 30,
|
|
882
|
+
.code = 40,
|
|
883
|
+
.flags = .{
|
|
884
|
+
.credits_must_not_exceed_debits = true,
|
|
885
|
+
},
|
|
886
|
+
.debits_pending = 50,
|
|
887
|
+
.debits_posted = 60,
|
|
888
|
+
.credits_pending = 1,
|
|
889
|
+
.credits_posted = 109,
|
|
890
|
+
.timestamp = 2,
|
|
645
891
|
}),
|
|
646
892
|
},
|
|
647
|
-
|
|
648
|
-
.result = .
|
|
649
|
-
.object =
|
|
650
|
-
.id =
|
|
651
|
-
.
|
|
652
|
-
.
|
|
653
|
-
.
|
|
893
|
+
.{
|
|
894
|
+
.result = .exists_with_different_flags,
|
|
895
|
+
.object = mem.zeroInit(Account, .{
|
|
896
|
+
.id = 1,
|
|
897
|
+
.user_data = 20,
|
|
898
|
+
.ledger = 30,
|
|
899
|
+
.code = 40,
|
|
900
|
+
.flags = .{
|
|
901
|
+
.credits_must_not_exceed_debits = true,
|
|
902
|
+
},
|
|
903
|
+
.debits_pending = 50,
|
|
904
|
+
.debits_posted = 60,
|
|
905
|
+
.credits_pending = 0,
|
|
906
|
+
.credits_posted = 0,
|
|
907
|
+
.timestamp = 2,
|
|
654
908
|
}),
|
|
655
909
|
},
|
|
656
|
-
|
|
910
|
+
.{
|
|
911
|
+
.result = .exists_with_different_user_data,
|
|
912
|
+
.object = mem.zeroInit(Account, .{
|
|
913
|
+
.id = 1,
|
|
914
|
+
.user_data = 20,
|
|
915
|
+
.ledger = 30,
|
|
916
|
+
.code = 40,
|
|
917
|
+
.debits_pending = 50,
|
|
918
|
+
.debits_posted = 60,
|
|
919
|
+
.credits_pending = 70,
|
|
920
|
+
.credits_posted = 80,
|
|
921
|
+
.timestamp = 2,
|
|
922
|
+
}),
|
|
923
|
+
},
|
|
924
|
+
.{
|
|
925
|
+
.result = .exists_with_different_ledger,
|
|
926
|
+
.object = mem.zeroInit(Account, .{
|
|
927
|
+
.id = 1,
|
|
928
|
+
.user_data = 2,
|
|
929
|
+
.ledger = 30,
|
|
930
|
+
.code = 40,
|
|
931
|
+
.debits_pending = 50,
|
|
932
|
+
.debits_posted = 60,
|
|
933
|
+
.credits_pending = 70,
|
|
934
|
+
.credits_posted = 80,
|
|
935
|
+
.timestamp = 2,
|
|
936
|
+
}),
|
|
937
|
+
},
|
|
938
|
+
.{
|
|
657
939
|
.result = .exists_with_different_code,
|
|
658
|
-
.object =
|
|
659
|
-
.id =
|
|
660
|
-
.
|
|
661
|
-
.
|
|
662
|
-
.
|
|
940
|
+
.object = mem.zeroInit(Account, .{
|
|
941
|
+
.id = 1,
|
|
942
|
+
.user_data = 2,
|
|
943
|
+
.ledger = 3,
|
|
944
|
+
.code = 40,
|
|
945
|
+
.debits_pending = 50,
|
|
946
|
+
.debits_posted = 60,
|
|
947
|
+
.credits_pending = 70,
|
|
948
|
+
.credits_posted = 80,
|
|
949
|
+
.timestamp = 2,
|
|
663
950
|
}),
|
|
664
951
|
},
|
|
665
|
-
|
|
666
|
-
.result = .
|
|
667
|
-
.object =
|
|
668
|
-
.id =
|
|
669
|
-
.
|
|
670
|
-
.
|
|
952
|
+
.{
|
|
953
|
+
.result = .exists_with_different_debits_pending,
|
|
954
|
+
.object = mem.zeroInit(Account, .{
|
|
955
|
+
.id = 1,
|
|
956
|
+
.user_data = 2,
|
|
957
|
+
.ledger = 3,
|
|
958
|
+
.code = 4,
|
|
959
|
+
.debits_pending = 50,
|
|
960
|
+
.debits_posted = 60,
|
|
961
|
+
.credits_pending = 70,
|
|
962
|
+
.credits_posted = 80,
|
|
963
|
+
.timestamp = 2,
|
|
671
964
|
}),
|
|
672
965
|
},
|
|
673
|
-
|
|
674
|
-
.result = .
|
|
675
|
-
.object =
|
|
676
|
-
.id =
|
|
677
|
-
.
|
|
678
|
-
.
|
|
966
|
+
.{
|
|
967
|
+
.result = .exists_with_different_debits_posted,
|
|
968
|
+
.object = mem.zeroInit(Account, .{
|
|
969
|
+
.id = 1,
|
|
970
|
+
.user_data = 2,
|
|
971
|
+
.ledger = 3,
|
|
972
|
+
.code = 4,
|
|
973
|
+
.debits_pending = 5,
|
|
974
|
+
.debits_posted = 60,
|
|
975
|
+
.credits_pending = 70,
|
|
976
|
+
.credits_posted = 80,
|
|
977
|
+
.timestamp = 2,
|
|
679
978
|
}),
|
|
680
979
|
},
|
|
681
|
-
|
|
682
|
-
.result = .
|
|
683
|
-
.object =
|
|
684
|
-
.id =
|
|
685
|
-
.
|
|
686
|
-
.
|
|
980
|
+
.{
|
|
981
|
+
.result = .exists_with_different_credits_pending,
|
|
982
|
+
.object = mem.zeroInit(Account, .{
|
|
983
|
+
.id = 1,
|
|
984
|
+
.user_data = 2,
|
|
985
|
+
.ledger = 3,
|
|
986
|
+
.code = 4,
|
|
987
|
+
.debits_pending = 5,
|
|
988
|
+
.debits_posted = 6,
|
|
989
|
+
.credits_pending = 70,
|
|
990
|
+
.credits_posted = 80,
|
|
991
|
+
.timestamp = 2,
|
|
687
992
|
}),
|
|
688
993
|
},
|
|
689
|
-
|
|
690
|
-
.result = .
|
|
691
|
-
.object =
|
|
692
|
-
.id =
|
|
693
|
-
.
|
|
694
|
-
.
|
|
994
|
+
.{
|
|
995
|
+
.result = .exists_with_different_credits_posted,
|
|
996
|
+
.object = mem.zeroInit(Account, .{
|
|
997
|
+
.id = 1,
|
|
998
|
+
.user_data = 2,
|
|
999
|
+
.ledger = 3,
|
|
1000
|
+
.code = 4,
|
|
1001
|
+
.debits_pending = 5,
|
|
1002
|
+
.debits_posted = 6,
|
|
1003
|
+
.credits_pending = 7,
|
|
1004
|
+
.credits_posted = 80,
|
|
1005
|
+
.timestamp = 2,
|
|
1006
|
+
}),
|
|
1007
|
+
},
|
|
1008
|
+
.{
|
|
1009
|
+
.result = .exists,
|
|
1010
|
+
.object = mem.zeroInit(Account, .{
|
|
1011
|
+
.id = 1,
|
|
1012
|
+
.user_data = 2,
|
|
1013
|
+
.ledger = 3,
|
|
1014
|
+
.code = 4,
|
|
1015
|
+
.debits_pending = 5,
|
|
1016
|
+
.debits_posted = 6,
|
|
1017
|
+
.credits_pending = 7,
|
|
1018
|
+
.credits_posted = 8,
|
|
1019
|
+
.timestamp = 2,
|
|
695
1020
|
}),
|
|
696
1021
|
},
|
|
697
1022
|
};
|
|
698
1023
|
|
|
699
|
-
var state_machine = try StateMachine.init(allocator, vectors.len, 0, 0);
|
|
1024
|
+
var state_machine = try StateMachine.init(testing.allocator, vectors.len, 0, 0);
|
|
700
1025
|
defer state_machine.deinit();
|
|
701
1026
|
|
|
702
|
-
for (vectors) |vector| {
|
|
703
|
-
|
|
1027
|
+
for (vectors) |vector, i| {
|
|
1028
|
+
const result = state_machine.create_account(&vector.object);
|
|
1029
|
+
expectEqual(vector.result, result) catch |err| {
|
|
1030
|
+
print_test_vector(i, vector.result, result, vector.object, err);
|
|
1031
|
+
return err;
|
|
1032
|
+
};
|
|
1033
|
+
|
|
704
1034
|
if (vector.result == .ok) {
|
|
705
|
-
try
|
|
1035
|
+
try expectEqual(vector.object, state_machine.get_account(vector.object.id).?.*);
|
|
706
1036
|
}
|
|
707
1037
|
}
|
|
1038
|
+
|
|
1039
|
+
state_machine.create_account_rollback(&vectors[0].object);
|
|
1040
|
+
try expect(state_machine.get_account(vectors[0].object.id) == null);
|
|
708
1041
|
}
|
|
709
1042
|
|
|
710
1043
|
test "linked accounts" {
|
|
711
|
-
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
712
|
-
defer arena.deinit();
|
|
713
|
-
|
|
714
|
-
const allocator = arena.allocator();
|
|
715
|
-
|
|
716
1044
|
const accounts_max = 5;
|
|
717
1045
|
const transfers_max = 0;
|
|
718
|
-
const
|
|
1046
|
+
const transfers_pending_max = 0;
|
|
719
1047
|
|
|
720
1048
|
var accounts = [_]Account{
|
|
721
1049
|
// An individual event (successful):
|
|
722
|
-
|
|
1050
|
+
mem.zeroInit(Account, .{ .id = 7, .code = 1, .ledger = 1 }),
|
|
723
1051
|
|
|
724
1052
|
// A chain of 4 events (the last event in the chain closes the chain with linked=false):
|
|
725
|
-
// Commit/rollback
|
|
726
|
-
|
|
727
|
-
// Commit/rollback
|
|
728
|
-
|
|
729
|
-
// Fail with .exists
|
|
730
|
-
|
|
1053
|
+
// Commit/rollback.
|
|
1054
|
+
mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
|
|
1055
|
+
// Commit/rollback.
|
|
1056
|
+
mem.zeroInit(Account, .{ .id = 2, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
|
|
1057
|
+
// Fail with .exists.
|
|
1058
|
+
mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
|
|
731
1059
|
// Fail without committing.
|
|
732
|
-
|
|
1060
|
+
mem.zeroInit(Account, .{ .id = 3, .code = 1, .ledger = 1 }),
|
|
733
1061
|
|
|
734
1062
|
// An individual event (successful):
|
|
735
|
-
// This should not see any effect from the failed chain above
|
|
736
|
-
|
|
1063
|
+
// This should not see any effect from the failed chain above.
|
|
1064
|
+
mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 1 }),
|
|
737
1065
|
|
|
738
1066
|
// A chain of 2 events (the first event fails the chain):
|
|
739
|
-
|
|
740
|
-
|
|
1067
|
+
mem.zeroInit(Account, .{ .id = 1, .code = 2, .ledger = 1, .flags = .{ .linked = true } }),
|
|
1068
|
+
mem.zeroInit(Account, .{ .id = 2, .code = 1, .ledger = 1 }),
|
|
741
1069
|
|
|
742
1070
|
// An individual event (successful):
|
|
743
|
-
|
|
1071
|
+
mem.zeroInit(Account, .{ .id = 2, .code = 1, .ledger = 1 }),
|
|
744
1072
|
|
|
745
1073
|
// A chain of 2 events (the last event fails the chain):
|
|
746
|
-
|
|
747
|
-
|
|
1074
|
+
mem.zeroInit(Account, .{ .id = 3, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
|
|
1075
|
+
mem.zeroInit(Account, .{ .id = 1, .code = 1, .ledger = 2 }),
|
|
748
1076
|
|
|
749
1077
|
// A chain of 2 events (successful):
|
|
750
|
-
|
|
751
|
-
|
|
1078
|
+
mem.zeroInit(Account, .{ .id = 3, .code = 1, .ledger = 1, .flags = .{ .linked = true } }),
|
|
1079
|
+
mem.zeroInit(Account, .{ .id = 4, .code = 1, .ledger = 1 }),
|
|
752
1080
|
};
|
|
753
1081
|
|
|
754
|
-
var state_machine = try StateMachine.init(
|
|
1082
|
+
var state_machine = try StateMachine.init(
|
|
1083
|
+
testing.allocator,
|
|
1084
|
+
accounts_max,
|
|
1085
|
+
transfers_max,
|
|
1086
|
+
transfers_pending_max,
|
|
1087
|
+
);
|
|
755
1088
|
defer state_machine.deinit();
|
|
756
1089
|
|
|
757
|
-
const input =
|
|
758
|
-
|
|
1090
|
+
const input = mem.asBytes(&accounts);
|
|
1091
|
+
|
|
1092
|
+
const output = try testing.allocator.alloc(u8, 4096);
|
|
1093
|
+
defer testing.allocator.free(output);
|
|
759
1094
|
|
|
760
|
-
state_machine.prepare(
|
|
1095
|
+
_ = state_machine.prepare(.create_accounts, input);
|
|
761
1096
|
const size = state_machine.commit(0, .create_accounts, input, output);
|
|
762
|
-
const results =
|
|
1097
|
+
const results = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
|
|
763
1098
|
|
|
764
|
-
try
|
|
1099
|
+
try expectEqualSlices(
|
|
765
1100
|
CreateAccountsResult,
|
|
766
1101
|
&[_]CreateAccountsResult{
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
CreateAccountsResult{ .index = 9, .result = .linked_event_failed },
|
|
776
|
-
CreateAccountsResult{ .index = 10, .result = .exists_with_different_code },
|
|
1102
|
+
.{ .index = 1, .result = .linked_event_failed },
|
|
1103
|
+
.{ .index = 2, .result = .linked_event_failed },
|
|
1104
|
+
.{ .index = 3, .result = .exists },
|
|
1105
|
+
.{ .index = 4, .result = .linked_event_failed },
|
|
1106
|
+
.{ .index = 6, .result = .exists_with_different_flags },
|
|
1107
|
+
.{ .index = 7, .result = .linked_event_failed },
|
|
1108
|
+
.{ .index = 9, .result = .linked_event_failed },
|
|
1109
|
+
.{ .index = 10, .result = .exists_with_different_ledger },
|
|
777
1110
|
},
|
|
778
1111
|
results,
|
|
779
1112
|
);
|
|
780
1113
|
|
|
781
|
-
try
|
|
782
|
-
try
|
|
783
|
-
try
|
|
784
|
-
try
|
|
785
|
-
try
|
|
786
|
-
try
|
|
1114
|
+
try expectEqual(accounts[0], state_machine.get_account(accounts[0].id).?.*);
|
|
1115
|
+
try expectEqual(accounts[5], state_machine.get_account(accounts[5].id).?.*);
|
|
1116
|
+
try expectEqual(accounts[8], state_machine.get_account(accounts[8].id).?.*);
|
|
1117
|
+
try expectEqual(accounts[11], state_machine.get_account(accounts[11].id).?.*);
|
|
1118
|
+
try expectEqual(accounts[12], state_machine.get_account(accounts[12].id).?.*);
|
|
1119
|
+
try expectEqual(@as(u32, 5), state_machine.accounts.count());
|
|
787
1120
|
|
|
788
1121
|
// TODO How can we test that events were in fact rolled back in LIFO order?
|
|
789
1122
|
// All our rollback handlers appear to be commutative.
|
|
790
1123
|
}
|
|
791
1124
|
|
|
1125
|
+
// The goal is to ensure that:
|
|
1126
|
+
// 1. all CreateTransferResult enums are covered, with
|
|
1127
|
+
// 2. enums tested in the order that they are defined, for easier auditing of coverage, and that
|
|
1128
|
+
// 3. state machine logic cannot be reordered in any way, breaking determinism.
|
|
792
1129
|
test "create/lookup/rollback transfers" {
|
|
793
|
-
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
794
|
-
defer arena.deinit();
|
|
795
|
-
|
|
796
|
-
const allocator = arena.allocator();
|
|
797
|
-
|
|
798
1130
|
var accounts = [_]Account{
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1131
|
+
mem.zeroInit(Account, .{
|
|
1132
|
+
.id = 1,
|
|
1133
|
+
.ledger = 1,
|
|
1134
|
+
.code = 1,
|
|
1135
|
+
.debits_pending = 100,
|
|
1136
|
+
.debits_posted = 200,
|
|
1137
|
+
}),
|
|
1138
|
+
mem.zeroInit(Account, .{ .id = 2, .ledger = 2, .code = 2 }),
|
|
1139
|
+
mem.zeroInit(Account, .{
|
|
1140
|
+
.id = 3,
|
|
1141
|
+
.ledger = 1,
|
|
1142
|
+
.code = 1,
|
|
1143
|
+
.credits_pending = 110,
|
|
1144
|
+
.credits_posted = 210,
|
|
1145
|
+
}),
|
|
1146
|
+
mem.zeroInit(Account, .{
|
|
1147
|
+
.id = 4,
|
|
1148
|
+
.ledger = 1,
|
|
1149
|
+
.code = 1,
|
|
1150
|
+
.flags = .{ .debits_must_not_exceed_credits = true },
|
|
1151
|
+
.debits_pending = 20,
|
|
1152
|
+
.debits_posted = math.maxInt(u64) - 500 - 200,
|
|
1153
|
+
.credits_pending = 0,
|
|
1154
|
+
.credits_posted = math.maxInt(u64) - 500,
|
|
1155
|
+
}),
|
|
1156
|
+
mem.zeroInit(Account, .{
|
|
1157
|
+
.id = 5,
|
|
1158
|
+
.ledger = 1,
|
|
1159
|
+
.code = 1,
|
|
1160
|
+
.flags = .{ .credits_must_not_exceed_debits = true },
|
|
1161
|
+
.debits_pending = 0,
|
|
1162
|
+
.debits_posted = math.maxInt(u64) - 1000,
|
|
1163
|
+
.credits_pending = 10,
|
|
1164
|
+
.credits_posted = math.maxInt(u64) - 1000 - 100,
|
|
1165
|
+
}),
|
|
807
1166
|
};
|
|
808
1167
|
|
|
809
|
-
var state_machine = try StateMachine.init(allocator, accounts.len, 1, 0);
|
|
1168
|
+
var state_machine = try StateMachine.init(testing.allocator, accounts.len, 1, 0);
|
|
810
1169
|
defer state_machine.deinit();
|
|
811
1170
|
|
|
812
|
-
const input =
|
|
813
|
-
const output = try allocator.alloc(u8, 4096);
|
|
1171
|
+
const input = mem.asBytes(&accounts);
|
|
814
1172
|
|
|
815
|
-
|
|
1173
|
+
const output = try testing.allocator.alloc(u8, 4096);
|
|
1174
|
+
defer testing.allocator.free(output);
|
|
1175
|
+
|
|
1176
|
+
_ = state_machine.prepare(.create_accounts, input);
|
|
816
1177
|
const size = state_machine.commit(0, .create_accounts, input, output);
|
|
817
1178
|
|
|
818
|
-
const errors =
|
|
819
|
-
try
|
|
1179
|
+
const errors = mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
|
|
1180
|
+
try expect(errors.len == 0);
|
|
820
1181
|
|
|
821
1182
|
for (accounts) |account| {
|
|
822
|
-
try
|
|
1183
|
+
try expectEqual(account, state_machine.get_account(account.id).?.*);
|
|
823
1184
|
}
|
|
824
1185
|
|
|
825
1186
|
const Vector = struct { result: CreateTransferResult, object: Transfer };
|
|
826
1187
|
|
|
827
1188
|
const timestamp: u64 = (state_machine.commit_timestamp + 1);
|
|
828
1189
|
const vectors = [_]Vector{
|
|
829
|
-
|
|
830
|
-
.result = .
|
|
831
|
-
.object =
|
|
1190
|
+
.{
|
|
1191
|
+
.result = .reserved_flag,
|
|
1192
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1193
|
+
.id = 0,
|
|
1194
|
+
.debit_account_id = 0,
|
|
1195
|
+
.credit_account_id = 0,
|
|
1196
|
+
.reserved = 1,
|
|
1197
|
+
.pending_id = 1,
|
|
1198
|
+
.timeout = 0,
|
|
1199
|
+
.ledger = 0,
|
|
1200
|
+
.code = 0,
|
|
1201
|
+
.flags = .{ .pending = true, .padding = 1 },
|
|
1202
|
+
.amount = 0,
|
|
1203
|
+
.timestamp = timestamp,
|
|
1204
|
+
}),
|
|
1205
|
+
},
|
|
1206
|
+
.{
|
|
1207
|
+
.result = .reserved_field,
|
|
1208
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1209
|
+
.id = 0,
|
|
1210
|
+
.debit_account_id = 0,
|
|
1211
|
+
.credit_account_id = 0,
|
|
1212
|
+
.reserved = 1,
|
|
1213
|
+
.pending_id = 1,
|
|
1214
|
+
.timeout = 0,
|
|
1215
|
+
.ledger = 0,
|
|
1216
|
+
.code = 0,
|
|
1217
|
+
.flags = .{ .pending = true },
|
|
1218
|
+
.amount = 0,
|
|
1219
|
+
.timestamp = timestamp,
|
|
1220
|
+
}),
|
|
1221
|
+
},
|
|
1222
|
+
.{
|
|
1223
|
+
.result = .id_must_not_be_zero,
|
|
1224
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1225
|
+
.id = 0,
|
|
1226
|
+
.debit_account_id = 0,
|
|
1227
|
+
.credit_account_id = 0,
|
|
1228
|
+
.pending_id = 1,
|
|
1229
|
+
.timeout = 0,
|
|
1230
|
+
.ledger = 0,
|
|
1231
|
+
.code = 0,
|
|
1232
|
+
.flags = .{ .pending = true },
|
|
1233
|
+
.amount = 0,
|
|
1234
|
+
.timestamp = timestamp,
|
|
1235
|
+
}),
|
|
1236
|
+
},
|
|
1237
|
+
.{
|
|
1238
|
+
.result = .debit_account_id_must_not_be_zero,
|
|
1239
|
+
.object = mem.zeroInit(Transfer, .{
|
|
832
1240
|
.id = 1,
|
|
1241
|
+
.debit_account_id = 0,
|
|
1242
|
+
.credit_account_id = 0,
|
|
1243
|
+
.pending_id = 1,
|
|
1244
|
+
.timeout = 0,
|
|
1245
|
+
.ledger = 0,
|
|
1246
|
+
.code = 0,
|
|
1247
|
+
.flags = .{ .pending = true },
|
|
1248
|
+
.amount = 0,
|
|
833
1249
|
.timestamp = timestamp,
|
|
834
1250
|
}),
|
|
835
1251
|
},
|
|
836
|
-
|
|
837
|
-
.result = .
|
|
838
|
-
.object =
|
|
839
|
-
.id =
|
|
1252
|
+
.{
|
|
1253
|
+
.result = .credit_account_id_must_not_be_zero,
|
|
1254
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1255
|
+
.id = 1,
|
|
1256
|
+
.debit_account_id = 100,
|
|
1257
|
+
.credit_account_id = 0,
|
|
1258
|
+
.pending_id = 1,
|
|
1259
|
+
.timeout = 0,
|
|
1260
|
+
.ledger = 0,
|
|
1261
|
+
.code = 0,
|
|
1262
|
+
.flags = .{ .pending = true },
|
|
1263
|
+
.amount = 0,
|
|
840
1264
|
.timestamp = timestamp,
|
|
841
|
-
.flags = .{ .padding = 1 },
|
|
842
1265
|
}),
|
|
843
1266
|
},
|
|
844
|
-
|
|
845
|
-
.result = .
|
|
846
|
-
.object =
|
|
847
|
-
.id =
|
|
1267
|
+
.{
|
|
1268
|
+
.result = .accounts_must_be_different,
|
|
1269
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1270
|
+
.id = 1,
|
|
1271
|
+
.debit_account_id = 100,
|
|
1272
|
+
.credit_account_id = 100,
|
|
1273
|
+
.pending_id = 1,
|
|
1274
|
+
.timeout = 0,
|
|
1275
|
+
.ledger = 0,
|
|
1276
|
+
.code = 0,
|
|
1277
|
+
.flags = .{ .pending = true },
|
|
1278
|
+
.amount = 0,
|
|
848
1279
|
.timestamp = timestamp,
|
|
849
|
-
.flags = .{ .two_phase_commit = true },
|
|
850
1280
|
}),
|
|
851
1281
|
},
|
|
852
|
-
|
|
853
|
-
.result = .
|
|
854
|
-
.object =
|
|
855
|
-
.id =
|
|
1282
|
+
.{
|
|
1283
|
+
.result = .pending_id_must_be_zero,
|
|
1284
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1285
|
+
.id = 1,
|
|
1286
|
+
.debit_account_id = 100,
|
|
1287
|
+
.credit_account_id = 200,
|
|
1288
|
+
.pending_id = 1,
|
|
1289
|
+
.timeout = 0,
|
|
1290
|
+
.ledger = 0,
|
|
1291
|
+
.code = 0,
|
|
1292
|
+
.flags = .{ .pending = true },
|
|
1293
|
+
.amount = 0,
|
|
856
1294
|
.timestamp = timestamp,
|
|
1295
|
+
}),
|
|
1296
|
+
},
|
|
1297
|
+
.{
|
|
1298
|
+
.result = .pending_transfer_must_timeout,
|
|
1299
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1300
|
+
.id = 1,
|
|
1301
|
+
.debit_account_id = 100,
|
|
1302
|
+
.credit_account_id = 200,
|
|
1303
|
+
.timeout = 0,
|
|
1304
|
+
.ledger = 0,
|
|
1305
|
+
.code = 0,
|
|
1306
|
+
.flags = .{ .pending = true },
|
|
1307
|
+
.amount = 0,
|
|
1308
|
+
.timestamp = timestamp,
|
|
1309
|
+
}),
|
|
1310
|
+
},
|
|
1311
|
+
.{
|
|
1312
|
+
.result = .timeout_reserved_for_pending_transfer,
|
|
1313
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1314
|
+
.id = 1,
|
|
1315
|
+
.debit_account_id = 100,
|
|
1316
|
+
.credit_account_id = 200,
|
|
857
1317
|
.timeout = 1,
|
|
1318
|
+
.ledger = 0,
|
|
1319
|
+
.code = 0,
|
|
1320
|
+
.amount = 0,
|
|
1321
|
+
.timestamp = timestamp,
|
|
858
1322
|
}),
|
|
859
1323
|
},
|
|
860
|
-
|
|
861
|
-
.result = .
|
|
862
|
-
.object =
|
|
863
|
-
.id =
|
|
1324
|
+
.{
|
|
1325
|
+
.result = .ledger_must_not_be_zero,
|
|
1326
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1327
|
+
.id = 1,
|
|
1328
|
+
.debit_account_id = 100,
|
|
1329
|
+
.credit_account_id = 200,
|
|
1330
|
+
.timeout = 1,
|
|
1331
|
+
.ledger = 0,
|
|
1332
|
+
.code = 0,
|
|
1333
|
+
.flags = .{ .pending = true },
|
|
1334
|
+
.amount = 0,
|
|
864
1335
|
.timestamp = timestamp,
|
|
865
|
-
.flags = .{ .condition = false },
|
|
866
|
-
.reserved = [_]u8{1} ** 32,
|
|
867
1336
|
}),
|
|
868
1337
|
},
|
|
869
|
-
|
|
870
|
-
.result = .
|
|
871
|
-
.object =
|
|
872
|
-
.id =
|
|
1338
|
+
.{
|
|
1339
|
+
.result = .code_must_not_be_zero,
|
|
1340
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1341
|
+
.id = 1,
|
|
1342
|
+
.debit_account_id = 100,
|
|
1343
|
+
.credit_account_id = 200,
|
|
1344
|
+
.timeout = 1,
|
|
1345
|
+
.ledger = 100,
|
|
1346
|
+
.code = 0,
|
|
1347
|
+
.flags = .{ .pending = true },
|
|
1348
|
+
.amount = 0,
|
|
873
1349
|
.timestamp = timestamp,
|
|
874
|
-
.amount = 10,
|
|
875
|
-
.debit_account_id = 1,
|
|
876
|
-
.credit_account_id = 1,
|
|
877
1350
|
}),
|
|
878
1351
|
},
|
|
879
|
-
|
|
880
|
-
.result = .
|
|
881
|
-
.object =
|
|
882
|
-
.id =
|
|
1352
|
+
.{
|
|
1353
|
+
.result = .amount_must_not_be_zero,
|
|
1354
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1355
|
+
.id = 1,
|
|
1356
|
+
.debit_account_id = 100,
|
|
1357
|
+
.credit_account_id = 200,
|
|
1358
|
+
.timeout = 1,
|
|
1359
|
+
.ledger = 100,
|
|
1360
|
+
.code = 1,
|
|
1361
|
+
.flags = .{ .pending = true },
|
|
1362
|
+
.amount = 0,
|
|
883
1363
|
.timestamp = timestamp,
|
|
884
|
-
|
|
1364
|
+
}),
|
|
1365
|
+
},
|
|
1366
|
+
.{
|
|
1367
|
+
.result = .debit_account_not_found,
|
|
1368
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1369
|
+
.id = 1,
|
|
885
1370
|
.debit_account_id = 100,
|
|
886
|
-
.credit_account_id =
|
|
1371
|
+
.credit_account_id = 200,
|
|
1372
|
+
.timeout = 1,
|
|
1373
|
+
.ledger = 100,
|
|
1374
|
+
.code = 1,
|
|
1375
|
+
.flags = .{ .pending = true },
|
|
1376
|
+
.amount = 100,
|
|
1377
|
+
.timestamp = timestamp,
|
|
887
1378
|
}),
|
|
888
1379
|
},
|
|
889
|
-
|
|
1380
|
+
.{
|
|
890
1381
|
.result = .credit_account_not_found,
|
|
891
|
-
.object =
|
|
892
|
-
.id =
|
|
1382
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1383
|
+
.id = 1,
|
|
1384
|
+
.debit_account_id = 1,
|
|
1385
|
+
.credit_account_id = 200,
|
|
1386
|
+
.timeout = 1,
|
|
1387
|
+
.ledger = 100,
|
|
1388
|
+
.code = 1,
|
|
1389
|
+
.flags = .{ .pending = true },
|
|
1390
|
+
.amount = 100,
|
|
893
1391
|
.timestamp = timestamp,
|
|
894
|
-
|
|
1392
|
+
}),
|
|
1393
|
+
},
|
|
1394
|
+
.{
|
|
1395
|
+
.result = .accounts_must_have_the_same_ledger,
|
|
1396
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1397
|
+
.id = 1,
|
|
895
1398
|
.debit_account_id = 1,
|
|
896
|
-
.credit_account_id =
|
|
1399
|
+
.credit_account_id = 2,
|
|
1400
|
+
.ledger = 100,
|
|
1401
|
+
.code = 1,
|
|
1402
|
+
.amount = 1,
|
|
1403
|
+
.timestamp = timestamp,
|
|
897
1404
|
}),
|
|
898
1405
|
},
|
|
899
|
-
|
|
900
|
-
.result = .
|
|
901
|
-
.object =
|
|
902
|
-
.id =
|
|
1406
|
+
.{
|
|
1407
|
+
.result = .transfer_must_have_the_same_ledger_as_accounts,
|
|
1408
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1409
|
+
.id = 1,
|
|
1410
|
+
.debit_account_id = 1,
|
|
1411
|
+
.credit_account_id = 3,
|
|
1412
|
+
.ledger = 100,
|
|
1413
|
+
.code = 1,
|
|
1414
|
+
.amount = 1,
|
|
903
1415
|
.timestamp = timestamp,
|
|
904
|
-
.amount = 10,
|
|
905
|
-
.debit_account_id = 3,
|
|
906
|
-
.credit_account_id = 4,
|
|
907
1416
|
}),
|
|
908
1417
|
},
|
|
909
|
-
|
|
910
|
-
.result = .
|
|
911
|
-
.object =
|
|
912
|
-
.id =
|
|
1418
|
+
.{
|
|
1419
|
+
.result = .overflows_debits_pending,
|
|
1420
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1421
|
+
.id = 1,
|
|
1422
|
+
.debit_account_id = 1,
|
|
1423
|
+
.credit_account_id = 3,
|
|
1424
|
+
.timeout = 30000,
|
|
1425
|
+
.ledger = 1,
|
|
1426
|
+
.code = 1,
|
|
1427
|
+
.flags = .{ .pending = true },
|
|
1428
|
+
.amount = math.maxInt(u64) - accounts[1 - 1].debits_pending + 1,
|
|
913
1429
|
.timestamp = timestamp,
|
|
914
|
-
.amount = 1000,
|
|
915
|
-
.debit_account_id = 5,
|
|
916
|
-
.credit_account_id = 1,
|
|
917
1430
|
}),
|
|
918
1431
|
},
|
|
919
|
-
|
|
920
|
-
.result = .
|
|
921
|
-
.object =
|
|
922
|
-
.id =
|
|
1432
|
+
.{
|
|
1433
|
+
.result = .overflows_credits_pending,
|
|
1434
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1435
|
+
.id = 1,
|
|
1436
|
+
.debit_account_id = 1,
|
|
1437
|
+
.credit_account_id = 3,
|
|
1438
|
+
.timeout = 30000,
|
|
1439
|
+
.ledger = 1,
|
|
1440
|
+
.code = 1,
|
|
1441
|
+
.flags = .{ .pending = true },
|
|
1442
|
+
.amount = math.maxInt(u64) - accounts[3 - 1].credits_pending + 1,
|
|
923
1443
|
.timestamp = timestamp,
|
|
924
|
-
|
|
1444
|
+
}),
|
|
1445
|
+
},
|
|
1446
|
+
.{
|
|
1447
|
+
.result = .overflows_debits_posted,
|
|
1448
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1449
|
+
.id = 1,
|
|
925
1450
|
.debit_account_id = 1,
|
|
926
|
-
.credit_account_id =
|
|
1451
|
+
.credit_account_id = 3,
|
|
1452
|
+
.ledger = 1,
|
|
1453
|
+
.code = 1,
|
|
1454
|
+
.amount = math.maxInt(u64) - accounts[1 - 1].debits_posted + 1,
|
|
1455
|
+
.timestamp = timestamp,
|
|
927
1456
|
}),
|
|
928
1457
|
},
|
|
929
|
-
|
|
930
|
-
.result = .
|
|
931
|
-
.object =
|
|
932
|
-
.id =
|
|
1458
|
+
.{
|
|
1459
|
+
.result = .overflows_credits_posted,
|
|
1460
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1461
|
+
.id = 1,
|
|
1462
|
+
.debit_account_id = 1,
|
|
1463
|
+
.credit_account_id = 3,
|
|
1464
|
+
.ledger = 1,
|
|
1465
|
+
.code = 1,
|
|
1466
|
+
.amount = math.maxInt(u64) - accounts[3 - 1].credits_posted + 1,
|
|
933
1467
|
.timestamp = timestamp,
|
|
934
|
-
.amount = 10,
|
|
935
|
-
.debit_account_id = 7,
|
|
936
|
-
.credit_account_id = 8,
|
|
937
1468
|
}),
|
|
938
1469
|
},
|
|
939
|
-
|
|
940
|
-
.result = .
|
|
941
|
-
.object =
|
|
942
|
-
.id =
|
|
943
|
-
.
|
|
944
|
-
.
|
|
945
|
-
.
|
|
946
|
-
.
|
|
1470
|
+
.{
|
|
1471
|
+
.result = .overflows_debits,
|
|
1472
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1473
|
+
.id = 1,
|
|
1474
|
+
.debit_account_id = 1,
|
|
1475
|
+
.credit_account_id = 3,
|
|
1476
|
+
.ledger = 1,
|
|
1477
|
+
.code = 1,
|
|
1478
|
+
.amount = math.maxInt(u64) -
|
|
1479
|
+
accounts[1 - 1].debits_pending -
|
|
1480
|
+
accounts[1 - 1].debits_posted + 1,
|
|
1481
|
+
.timestamp = timestamp,
|
|
947
1482
|
}),
|
|
948
1483
|
},
|
|
949
|
-
|
|
950
|
-
.result = .
|
|
951
|
-
.object =
|
|
952
|
-
.id =
|
|
953
|
-
.
|
|
954
|
-
.
|
|
955
|
-
.
|
|
956
|
-
.
|
|
1484
|
+
.{
|
|
1485
|
+
.result = .overflows_credits,
|
|
1486
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1487
|
+
.id = 1,
|
|
1488
|
+
.debit_account_id = 1,
|
|
1489
|
+
.credit_account_id = 3,
|
|
1490
|
+
.ledger = 1,
|
|
1491
|
+
.code = 1,
|
|
1492
|
+
.amount = math.maxInt(u64) -
|
|
1493
|
+
accounts[3 - 1].credits_pending -
|
|
1494
|
+
accounts[3 - 1].credits_posted + 1,
|
|
1495
|
+
.timestamp = timestamp,
|
|
957
1496
|
}),
|
|
958
1497
|
},
|
|
959
|
-
|
|
960
|
-
.result = .
|
|
961
|
-
.object =
|
|
962
|
-
.id =
|
|
963
|
-
.
|
|
964
|
-
.
|
|
965
|
-
.
|
|
966
|
-
.
|
|
1498
|
+
.{
|
|
1499
|
+
.result = .exceeds_credits,
|
|
1500
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1501
|
+
.id = 1,
|
|
1502
|
+
.debit_account_id = 4,
|
|
1503
|
+
.credit_account_id = 5,
|
|
1504
|
+
.ledger = 1,
|
|
1505
|
+
.code = 1,
|
|
1506
|
+
.amount = accounts[4 - 1].credits_posted -
|
|
1507
|
+
accounts[4 - 1].debits_pending -
|
|
1508
|
+
accounts[4 - 1].debits_posted + 1,
|
|
1509
|
+
.timestamp = timestamp,
|
|
967
1510
|
}),
|
|
968
1511
|
},
|
|
969
|
-
|
|
970
|
-
.result = .
|
|
971
|
-
.object =
|
|
972
|
-
.id =
|
|
1512
|
+
.{
|
|
1513
|
+
.result = .exceeds_debits,
|
|
1514
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1515
|
+
.id = 1,
|
|
1516
|
+
.debit_account_id = 4,
|
|
1517
|
+
.credit_account_id = 5,
|
|
1518
|
+
.ledger = 1,
|
|
1519
|
+
.code = 1,
|
|
1520
|
+
.amount = accounts[5 - 1].debits_posted -
|
|
1521
|
+
accounts[5 - 1].credits_pending -
|
|
1522
|
+
accounts[5 - 1].credits_posted + 1,
|
|
1523
|
+
.timestamp = timestamp,
|
|
1524
|
+
}),
|
|
1525
|
+
},
|
|
1526
|
+
.{
|
|
1527
|
+
.result = .ok,
|
|
1528
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1529
|
+
.id = 1,
|
|
1530
|
+
.debit_account_id = 1,
|
|
1531
|
+
.credit_account_id = 3,
|
|
1532
|
+
.timeout = 10000,
|
|
1533
|
+
.ledger = 1,
|
|
1534
|
+
.code = 1,
|
|
1535
|
+
.flags = .{ .pending = true },
|
|
1536
|
+
.amount = 123,
|
|
973
1537
|
.timestamp = timestamp + 1,
|
|
974
|
-
.amount = 11,
|
|
975
|
-
.debit_account_id = 7,
|
|
976
|
-
.credit_account_id = 8,
|
|
977
1538
|
}),
|
|
978
1539
|
},
|
|
979
|
-
|
|
1540
|
+
.{
|
|
1541
|
+
// Ensure that idempotence is only checked after validation.
|
|
1542
|
+
.result = .transfer_must_have_the_same_ledger_as_accounts,
|
|
1543
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1544
|
+
.id = 1,
|
|
1545
|
+
.debit_account_id = 1,
|
|
1546
|
+
.credit_account_id = 3,
|
|
1547
|
+
.timeout = 10000,
|
|
1548
|
+
.ledger = 2,
|
|
1549
|
+
.code = 1,
|
|
1550
|
+
.flags = .{ .pending = true },
|
|
1551
|
+
.amount = 123,
|
|
1552
|
+
.timestamp = timestamp + 2,
|
|
1553
|
+
}),
|
|
1554
|
+
},
|
|
1555
|
+
.{
|
|
980
1556
|
.result = .exists_with_different_flags,
|
|
981
|
-
.object =
|
|
982
|
-
.id =
|
|
983
|
-
.
|
|
984
|
-
.
|
|
985
|
-
.
|
|
986
|
-
.
|
|
987
|
-
.
|
|
1557
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1558
|
+
.id = 1,
|
|
1559
|
+
.debit_account_id = 1,
|
|
1560
|
+
.credit_account_id = 3,
|
|
1561
|
+
.user_data = 1,
|
|
1562
|
+
.ledger = 1,
|
|
1563
|
+
.code = 2,
|
|
1564
|
+
.flags = .{ .pending = false },
|
|
1565
|
+
.amount = math.maxInt(u64),
|
|
1566
|
+
.timestamp = timestamp + 2,
|
|
988
1567
|
}),
|
|
989
1568
|
},
|
|
990
|
-
|
|
1569
|
+
.{
|
|
1570
|
+
.result = .exists_with_different_debit_account_id,
|
|
1571
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1572
|
+
.id = 1,
|
|
1573
|
+
.debit_account_id = 3,
|
|
1574
|
+
.credit_account_id = 1,
|
|
1575
|
+
.user_data = 1,
|
|
1576
|
+
.timeout = 10000,
|
|
1577
|
+
.ledger = 1,
|
|
1578
|
+
.code = 2,
|
|
1579
|
+
.flags = .{ .pending = true },
|
|
1580
|
+
.amount = math.maxInt(u64),
|
|
1581
|
+
.timestamp = timestamp + 2,
|
|
1582
|
+
}),
|
|
1583
|
+
},
|
|
1584
|
+
.{
|
|
1585
|
+
.result = .exists_with_different_credit_account_id,
|
|
1586
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1587
|
+
.id = 1,
|
|
1588
|
+
.debit_account_id = 1,
|
|
1589
|
+
.credit_account_id = 4,
|
|
1590
|
+
.user_data = 1,
|
|
1591
|
+
.timeout = 10000,
|
|
1592
|
+
.ledger = 1,
|
|
1593
|
+
.code = 2,
|
|
1594
|
+
.flags = .{ .pending = true },
|
|
1595
|
+
.amount = math.maxInt(u64),
|
|
1596
|
+
.timestamp = timestamp + 2,
|
|
1597
|
+
}),
|
|
1598
|
+
},
|
|
1599
|
+
.{
|
|
991
1600
|
.result = .exists_with_different_user_data,
|
|
992
|
-
.object =
|
|
993
|
-
.id =
|
|
994
|
-
.
|
|
995
|
-
.
|
|
996
|
-
.
|
|
997
|
-
.
|
|
998
|
-
.
|
|
1601
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1602
|
+
.id = 1,
|
|
1603
|
+
.debit_account_id = 1,
|
|
1604
|
+
.credit_account_id = 3,
|
|
1605
|
+
.user_data = 1,
|
|
1606
|
+
.timeout = 10000,
|
|
1607
|
+
.ledger = 1,
|
|
1608
|
+
.code = 2,
|
|
1609
|
+
.flags = .{ .pending = true },
|
|
1610
|
+
.amount = math.maxInt(u64),
|
|
1611
|
+
.timestamp = timestamp + 2,
|
|
999
1612
|
}),
|
|
1000
1613
|
},
|
|
1001
|
-
|
|
1002
|
-
.result = .
|
|
1003
|
-
.object =
|
|
1004
|
-
.id =
|
|
1005
|
-
.
|
|
1006
|
-
.
|
|
1007
|
-
.
|
|
1008
|
-
.
|
|
1009
|
-
.
|
|
1010
|
-
.
|
|
1614
|
+
.{
|
|
1615
|
+
.result = .exists_with_different_timeout,
|
|
1616
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1617
|
+
.id = 1,
|
|
1618
|
+
.debit_account_id = 1,
|
|
1619
|
+
.credit_account_id = 3,
|
|
1620
|
+
.timeout = 10001,
|
|
1621
|
+
.ledger = 1,
|
|
1622
|
+
.code = 2,
|
|
1623
|
+
.flags = .{ .pending = true },
|
|
1624
|
+
.amount = math.maxInt(u64),
|
|
1625
|
+
.timestamp = timestamp + 2,
|
|
1011
1626
|
}),
|
|
1012
1627
|
},
|
|
1013
|
-
|
|
1014
|
-
.result = .
|
|
1015
|
-
.object =
|
|
1016
|
-
.id =
|
|
1628
|
+
.{
|
|
1629
|
+
.result = .exists_with_different_code,
|
|
1630
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1631
|
+
.id = 1,
|
|
1632
|
+
.debit_account_id = 1,
|
|
1633
|
+
.credit_account_id = 3,
|
|
1634
|
+
.timeout = 10000,
|
|
1635
|
+
.ledger = 1,
|
|
1636
|
+
.code = 2,
|
|
1637
|
+
.flags = .{ .pending = true },
|
|
1638
|
+
.amount = math.maxInt(u64),
|
|
1017
1639
|
.timestamp = timestamp + 2,
|
|
1018
|
-
.amount = 10,
|
|
1019
|
-
.debit_account_id = 7,
|
|
1020
|
-
.credit_account_id = 8,
|
|
1021
|
-
.flags = .{ .condition = true },
|
|
1022
|
-
.reserved = [_]u8{2} ** 32,
|
|
1023
1640
|
}),
|
|
1024
1641
|
},
|
|
1025
|
-
|
|
1026
|
-
.result = .
|
|
1027
|
-
.object =
|
|
1028
|
-
.id =
|
|
1642
|
+
.{
|
|
1643
|
+
.result = .exists_with_different_amount,
|
|
1644
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1645
|
+
.id = 1,
|
|
1646
|
+
.debit_account_id = 1,
|
|
1647
|
+
.credit_account_id = 3,
|
|
1648
|
+
.timeout = 10000,
|
|
1649
|
+
.ledger = 1,
|
|
1650
|
+
.code = 1,
|
|
1651
|
+
.flags = .{ .pending = true },
|
|
1652
|
+
.amount = math.maxInt(u64),
|
|
1029
1653
|
.timestamp = timestamp + 2,
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
.
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
.
|
|
1654
|
+
}),
|
|
1655
|
+
},
|
|
1656
|
+
.{
|
|
1657
|
+
.result = .exists,
|
|
1658
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1659
|
+
.id = 1,
|
|
1660
|
+
.debit_account_id = 1,
|
|
1661
|
+
.credit_account_id = 3,
|
|
1662
|
+
.timeout = 10000,
|
|
1663
|
+
.ledger = 1,
|
|
1664
|
+
.code = 1,
|
|
1665
|
+
.flags = .{ .pending = true },
|
|
1666
|
+
.amount = 123,
|
|
1042
1667
|
.timestamp = timestamp + 2,
|
|
1043
|
-
.amount = 10,
|
|
1044
|
-
.debit_account_id = 7,
|
|
1045
|
-
.credit_account_id = 8,
|
|
1046
|
-
.flags = .{ .two_phase_commit = true },
|
|
1047
|
-
.timeout = 0,
|
|
1048
1668
|
}),
|
|
1049
1669
|
},
|
|
1050
|
-
|
|
1670
|
+
.{
|
|
1051
1671
|
.result = .ok,
|
|
1052
|
-
.object =
|
|
1053
|
-
.id =
|
|
1672
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1673
|
+
.id = 2,
|
|
1674
|
+
.debit_account_id = 3,
|
|
1675
|
+
.credit_account_id = 1,
|
|
1676
|
+
.ledger = 1,
|
|
1677
|
+
.code = 2,
|
|
1678
|
+
.amount = 7,
|
|
1054
1679
|
.timestamp = timestamp + 2,
|
|
1055
|
-
.amount = 10,
|
|
1056
|
-
.debit_account_id = 7,
|
|
1057
|
-
.credit_account_id = 8,
|
|
1058
|
-
.flags = .{ .two_phase_commit = true },
|
|
1059
|
-
.timeout = 20,
|
|
1060
1680
|
}),
|
|
1061
1681
|
},
|
|
1062
|
-
|
|
1063
|
-
.result = .
|
|
1064
|
-
.object =
|
|
1065
|
-
.id =
|
|
1682
|
+
.{
|
|
1683
|
+
.result = .ok,
|
|
1684
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1685
|
+
.id = 3,
|
|
1686
|
+
.debit_account_id = 1,
|
|
1687
|
+
.credit_account_id = 3,
|
|
1688
|
+
.ledger = 1,
|
|
1689
|
+
.code = 2,
|
|
1690
|
+
.amount = 3,
|
|
1066
1691
|
.timestamp = timestamp + 3,
|
|
1067
|
-
.amount = 10,
|
|
1068
|
-
.debit_account_id = 7,
|
|
1069
|
-
.credit_account_id = 8,
|
|
1070
|
-
.flags = .{ .two_phase_commit = true },
|
|
1071
|
-
.timeout = 25,
|
|
1072
1692
|
}),
|
|
1073
1693
|
},
|
|
1074
1694
|
};
|
|
1075
1695
|
|
|
1076
|
-
for (vectors) |vector| {
|
|
1077
|
-
|
|
1696
|
+
for (vectors) |vector, i| {
|
|
1697
|
+
const result = state_machine.create_transfer(&vector.object);
|
|
1698
|
+
expectEqual(vector.result, result) catch |err| {
|
|
1699
|
+
print_test_vector(i, vector.result, result, vector.object, err);
|
|
1700
|
+
return err;
|
|
1701
|
+
};
|
|
1078
1702
|
if (vector.result == .ok) {
|
|
1079
|
-
try
|
|
1703
|
+
try expectEqual(vector.object, state_machine.get_transfer(vector.object.id).?.*);
|
|
1080
1704
|
}
|
|
1081
1705
|
}
|
|
1082
1706
|
|
|
1083
|
-
//
|
|
1084
|
-
try
|
|
1085
|
-
try
|
|
1086
|
-
|
|
1087
|
-
try
|
|
1088
|
-
|
|
1089
|
-
try
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
try
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
try
|
|
1097
|
-
try
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
try
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
try
|
|
1105
|
-
try
|
|
1106
|
-
try testing.expect(state_machine.get_transfer(vectors[22].object.id) == null);
|
|
1107
|
-
}
|
|
1707
|
+
// Transfer 3:
|
|
1708
|
+
try test_account_balances(&state_machine, 1, 100 + 123, 200 + 3, 0, 7);
|
|
1709
|
+
try test_account_balances(&state_machine, 3, 0, 7, 110 + 123, 210 + 3);
|
|
1710
|
+
state_machine.create_transfer_rollback(state_machine.get_transfer(3).?);
|
|
1711
|
+
try test_account_balances(&state_machine, 1, 100 + 123, 200, 0, 7);
|
|
1712
|
+
try test_account_balances(&state_machine, 3, 0, 7, 110 + 123, 210);
|
|
1713
|
+
try expect(state_machine.get_transfer(3) == null);
|
|
1714
|
+
|
|
1715
|
+
// Transfer 2:
|
|
1716
|
+
try test_account_balances(&state_machine, 1, 100 + 123, 200, 0, 7);
|
|
1717
|
+
try test_account_balances(&state_machine, 3, 0, 7, 110 + 123, 210);
|
|
1718
|
+
state_machine.create_transfer_rollback(state_machine.get_transfer(2).?);
|
|
1719
|
+
try test_account_balances(&state_machine, 1, 100 + 123, 200, 0, 0);
|
|
1720
|
+
try test_account_balances(&state_machine, 3, 0, 0, 110 + 123, 210);
|
|
1721
|
+
try expect(state_machine.get_transfer(2) == null);
|
|
1722
|
+
|
|
1723
|
+
// Transfer 1:
|
|
1724
|
+
try test_account_balances(&state_machine, 1, 100 + 123, 200, 0, 0);
|
|
1725
|
+
try test_account_balances(&state_machine, 3, 0, 0, 110 + 123, 210);
|
|
1726
|
+
state_machine.create_transfer_rollback(state_machine.get_transfer(1).?);
|
|
1727
|
+
try test_account_balances(&state_machine, 1, 100, 200, 0, 0);
|
|
1728
|
+
try test_account_balances(&state_machine, 3, 0, 0, 110, 210);
|
|
1729
|
+
try expect(state_machine.get_transfer(1) == null);
|
|
1108
1730
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
const Vector = struct { result: CommitTransferResult, object: Commit };
|
|
1731
|
+
for (accounts) |account| {
|
|
1732
|
+
state_machine.create_account_rollback(&account);
|
|
1733
|
+
try expect(state_machine.get_account(account.id) == null);
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1116
1736
|
|
|
1737
|
+
test "create/lookup/rollback 2-phase transfers" {
|
|
1117
1738
|
var accounts = [_]Account{
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
std.mem.zeroInit(Account, .{ .id = 3 }),
|
|
1121
|
-
std.mem.zeroInit(Account, .{ .id = 4 }),
|
|
1739
|
+
mem.zeroInit(Account, .{ .id = 1, .ledger = 1, .code = 1 }),
|
|
1740
|
+
mem.zeroInit(Account, .{ .id = 2, .ledger = 1, .code = 1 }),
|
|
1122
1741
|
};
|
|
1123
1742
|
|
|
1124
1743
|
var transfers = [_]Transfer{
|
|
1125
|
-
|
|
1744
|
+
mem.zeroInit(Transfer, .{
|
|
1126
1745
|
.id = 1,
|
|
1127
|
-
.amount = 15,
|
|
1128
1746
|
.debit_account_id = 1,
|
|
1129
1747
|
.credit_account_id = 2,
|
|
1748
|
+
.ledger = 1,
|
|
1749
|
+
.code = 1,
|
|
1750
|
+
.amount = 15,
|
|
1130
1751
|
}),
|
|
1131
|
-
|
|
1752
|
+
mem.zeroInit(Transfer, .{
|
|
1132
1753
|
.id = 2,
|
|
1133
|
-
.amount = 15,
|
|
1134
1754
|
.debit_account_id = 1,
|
|
1135
1755
|
.credit_account_id = 2,
|
|
1136
|
-
.
|
|
1137
|
-
.
|
|
1756
|
+
.timeout = 1000,
|
|
1757
|
+
.ledger = 1,
|
|
1758
|
+
.code = 1,
|
|
1759
|
+
.flags = .{ .pending = true },
|
|
1760
|
+
.amount = 15,
|
|
1138
1761
|
}),
|
|
1139
|
-
|
|
1762
|
+
mem.zeroInit(Transfer, .{
|
|
1140
1763
|
.id = 3,
|
|
1141
|
-
.amount = 15,
|
|
1142
1764
|
.debit_account_id = 1,
|
|
1143
1765
|
.credit_account_id = 2,
|
|
1144
|
-
.
|
|
1145
|
-
.
|
|
1766
|
+
.timeout = 5,
|
|
1767
|
+
.ledger = 1,
|
|
1768
|
+
.code = 1,
|
|
1769
|
+
.flags = .{ .pending = true },
|
|
1770
|
+
.amount = 15,
|
|
1146
1771
|
}),
|
|
1147
|
-
|
|
1772
|
+
mem.zeroInit(Transfer, .{
|
|
1148
1773
|
.id = 4,
|
|
1149
|
-
.amount = 15,
|
|
1150
1774
|
.debit_account_id = 1,
|
|
1151
1775
|
.credit_account_id = 2,
|
|
1152
|
-
.flags = .{ .two_phase_commit = true },
|
|
1153
1776
|
.timeout = 1,
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
.
|
|
1777
|
+
.ledger = 1,
|
|
1778
|
+
.code = 1,
|
|
1779
|
+
.flags = .{ .pending = true },
|
|
1157
1780
|
.amount = 15,
|
|
1158
|
-
.debit_account_id = 1,
|
|
1159
|
-
.credit_account_id = 2,
|
|
1160
|
-
.flags = .{
|
|
1161
|
-
.two_phase_commit = true,
|
|
1162
|
-
.condition = true,
|
|
1163
|
-
},
|
|
1164
|
-
.timeout = 25,
|
|
1165
1781
|
}),
|
|
1166
|
-
|
|
1167
|
-
.id =
|
|
1168
|
-
.amount = 15,
|
|
1782
|
+
mem.zeroInit(Transfer, .{
|
|
1783
|
+
.id = 5,
|
|
1169
1784
|
.debit_account_id = 1,
|
|
1170
1785
|
.credit_account_id = 2,
|
|
1171
|
-
.
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
.
|
|
1176
|
-
|
|
1177
|
-
std.mem.zeroInit(Transfer, .{
|
|
1178
|
-
.id = 7,
|
|
1179
|
-
.amount = 15,
|
|
1180
|
-
.debit_account_id = 3,
|
|
1181
|
-
.credit_account_id = 4,
|
|
1182
|
-
.flags = .{ .two_phase_commit = true },
|
|
1183
|
-
.timeout = 25,
|
|
1786
|
+
.user_data = 73,
|
|
1787
|
+
.timeout = 6,
|
|
1788
|
+
.ledger = 1,
|
|
1789
|
+
.code = 1,
|
|
1790
|
+
.flags = .{ .pending = true },
|
|
1791
|
+
.amount = 7,
|
|
1184
1792
|
}),
|
|
1185
1793
|
};
|
|
1186
1794
|
|
|
1187
|
-
var state_machine = try StateMachine.init(allocator, accounts.len,
|
|
1795
|
+
var state_machine = try StateMachine.init(testing.allocator, accounts.len, 100, 1);
|
|
1188
1796
|
defer state_machine.deinit();
|
|
1189
1797
|
|
|
1190
|
-
|
|
1191
|
-
const
|
|
1798
|
+
// Create accounts:
|
|
1799
|
+
const accounts_input = mem.asBytes(&accounts);
|
|
1192
1800
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1801
|
+
const accounts_output = try testing.allocator.alloc(u8, 4096);
|
|
1802
|
+
defer testing.allocator.free(accounts_output);
|
|
1803
|
+
|
|
1804
|
+
const accounts_timestamp = state_machine.prepare(.create_accounts, accounts_input);
|
|
1196
1805
|
{
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1806
|
+
const size = state_machine.commit(0, .create_accounts, accounts_input, accounts_output);
|
|
1807
|
+
const errors = mem.bytesAsSlice(CreateAccountsResult, accounts_output[0..size]);
|
|
1808
|
+
try expectEqual(@as(usize, 0), errors.len);
|
|
1199
1809
|
}
|
|
1200
|
-
|
|
1201
1810
|
for (accounts) |account| {
|
|
1202
|
-
try
|
|
1811
|
+
try expectEqual(account, state_machine.get_account(account.id).?.*);
|
|
1203
1812
|
}
|
|
1204
1813
|
|
|
1205
|
-
//
|
|
1206
|
-
const
|
|
1207
|
-
const output_transfers = try allocator.alloc(u8, 4096);
|
|
1814
|
+
// Create pending transfers:
|
|
1815
|
+
const transfers_input = mem.asBytes(&transfers);
|
|
1208
1816
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
0,
|
|
1212
|
-
.create_transfers,
|
|
1213
|
-
object_transfers,
|
|
1214
|
-
output_transfers,
|
|
1215
|
-
);
|
|
1216
|
-
const errors = std.mem.bytesAsSlice(CreateTransfersResult, output_transfers[0..size_transfers]);
|
|
1217
|
-
try testing.expectEqual(@as(usize, 0), errors.len);
|
|
1817
|
+
const transfers_output = try testing.allocator.alloc(u8, 4096);
|
|
1818
|
+
defer testing.allocator.free(transfers_output);
|
|
1218
1819
|
|
|
1820
|
+
const transfers_timestamp = state_machine.prepare(.create_transfers, transfers_input);
|
|
1821
|
+
try testing.expect(transfers_timestamp > accounts_timestamp);
|
|
1822
|
+
{
|
|
1823
|
+
const size = state_machine.commit(0, .create_transfers, transfers_input, transfers_output);
|
|
1824
|
+
const errors = mem.bytesAsSlice(CreateTransfersResult, transfers_output[0..size]);
|
|
1825
|
+
try expectEqual(@as(usize, 0), errors.len);
|
|
1826
|
+
}
|
|
1219
1827
|
for (transfers) |transfer| {
|
|
1220
|
-
try
|
|
1828
|
+
try expectEqual(transfer, state_machine.get_transfer(transfer.id).?.*);
|
|
1221
1829
|
}
|
|
1222
1830
|
|
|
1223
|
-
//
|
|
1831
|
+
// Test balances before posting:
|
|
1832
|
+
try test_account_balances(&state_machine, 1, 52, 15, 0, 0);
|
|
1833
|
+
try test_account_balances(&state_machine, 2, 0, 0, 52, 15);
|
|
1834
|
+
|
|
1835
|
+
// Post pending transfers:
|
|
1836
|
+
const Vector = struct { result: CreateTransferResult, object: Transfer };
|
|
1224
1837
|
const timestamp: u64 = (state_machine.commit_timestamp + 1);
|
|
1838
|
+
|
|
1225
1839
|
const vectors = [_]Vector{
|
|
1226
|
-
|
|
1227
|
-
.result = .
|
|
1228
|
-
.object =
|
|
1229
|
-
.id =
|
|
1840
|
+
.{
|
|
1841
|
+
.result = .ok,
|
|
1842
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1843
|
+
.id = 101,
|
|
1844
|
+
.debit_account_id = 1,
|
|
1845
|
+
.credit_account_id = 2,
|
|
1846
|
+
.user_data = 1000,
|
|
1847
|
+
.pending_id = 2,
|
|
1848
|
+
.ledger = 1,
|
|
1849
|
+
.code = 1,
|
|
1850
|
+
.flags = .{ .post_pending_transfer = true },
|
|
1851
|
+
.amount = 13,
|
|
1230
1852
|
.timestamp = timestamp,
|
|
1231
|
-
.reserved = [_]u8{1} ** 32,
|
|
1232
1853
|
}),
|
|
1233
1854
|
},
|
|
1234
|
-
|
|
1235
|
-
.result = .
|
|
1236
|
-
.object =
|
|
1237
|
-
.id =
|
|
1238
|
-
.
|
|
1239
|
-
.
|
|
1855
|
+
.{
|
|
1856
|
+
.result = .cannot_post_and_void_pending_transfer,
|
|
1857
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1858
|
+
.id = 101,
|
|
1859
|
+
.debit_account_id = 10,
|
|
1860
|
+
.credit_account_id = 20,
|
|
1861
|
+
.user_data = 30,
|
|
1862
|
+
.pending_id = 0,
|
|
1863
|
+
.timeout = 50,
|
|
1864
|
+
.ledger = 60,
|
|
1865
|
+
.code = 70,
|
|
1866
|
+
.flags = .{
|
|
1867
|
+
.pending = true,
|
|
1868
|
+
.post_pending_transfer = true,
|
|
1869
|
+
.void_pending_transfer = true,
|
|
1870
|
+
},
|
|
1871
|
+
.amount = 80,
|
|
1872
|
+
.timestamp = timestamp + 1,
|
|
1240
1873
|
}),
|
|
1241
1874
|
},
|
|
1242
|
-
|
|
1243
|
-
.result = .
|
|
1244
|
-
.object =
|
|
1245
|
-
.id =
|
|
1246
|
-
.
|
|
1875
|
+
.{
|
|
1876
|
+
.result = .pending_transfer_cannot_post_or_void_another,
|
|
1877
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1878
|
+
.id = 101,
|
|
1879
|
+
.debit_account_id = 10,
|
|
1880
|
+
.credit_account_id = 20,
|
|
1881
|
+
.user_data = 30,
|
|
1882
|
+
.pending_id = 0,
|
|
1883
|
+
.timeout = 50,
|
|
1884
|
+
.ledger = 60,
|
|
1885
|
+
.code = 70,
|
|
1886
|
+
.flags = .{
|
|
1887
|
+
.pending = true,
|
|
1888
|
+
.void_pending_transfer = true,
|
|
1889
|
+
},
|
|
1890
|
+
.amount = 80,
|
|
1891
|
+
.timestamp = timestamp + 1,
|
|
1247
1892
|
}),
|
|
1248
1893
|
},
|
|
1249
|
-
|
|
1250
|
-
.result = .
|
|
1251
|
-
.object =
|
|
1252
|
-
.id =
|
|
1253
|
-
.
|
|
1894
|
+
.{
|
|
1895
|
+
.result = .timeout_reserved_for_pending_transfer,
|
|
1896
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1897
|
+
.id = 101,
|
|
1898
|
+
.debit_account_id = 10,
|
|
1899
|
+
.credit_account_id = 20,
|
|
1900
|
+
.user_data = 30,
|
|
1901
|
+
.timeout = 50,
|
|
1902
|
+
.ledger = 60,
|
|
1903
|
+
.code = 70,
|
|
1904
|
+
.flags = .{
|
|
1905
|
+
.void_pending_transfer = true,
|
|
1906
|
+
},
|
|
1907
|
+
.amount = 80,
|
|
1908
|
+
.timestamp = timestamp + 1,
|
|
1254
1909
|
}),
|
|
1255
1910
|
},
|
|
1256
|
-
|
|
1257
|
-
.result = .
|
|
1258
|
-
.object =
|
|
1259
|
-
.id =
|
|
1260
|
-
.
|
|
1911
|
+
.{
|
|
1912
|
+
.result = .pending_id_must_not_be_zero,
|
|
1913
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1914
|
+
.id = 101,
|
|
1915
|
+
.debit_account_id = 10,
|
|
1916
|
+
.credit_account_id = 20,
|
|
1917
|
+
.user_data = 30,
|
|
1918
|
+
.pending_id = 0,
|
|
1919
|
+
.ledger = 60,
|
|
1920
|
+
.code = 70,
|
|
1921
|
+
.flags = .{
|
|
1922
|
+
.void_pending_transfer = true,
|
|
1923
|
+
},
|
|
1924
|
+
.amount = 80,
|
|
1925
|
+
.timestamp = timestamp + 1,
|
|
1261
1926
|
}),
|
|
1262
1927
|
},
|
|
1263
|
-
|
|
1264
|
-
.result = .
|
|
1265
|
-
.object =
|
|
1266
|
-
.id =
|
|
1928
|
+
.{
|
|
1929
|
+
.result = .pending_id_must_be_different,
|
|
1930
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1931
|
+
.id = 101,
|
|
1932
|
+
.debit_account_id = 10,
|
|
1933
|
+
.credit_account_id = 20,
|
|
1934
|
+
.user_data = 30,
|
|
1935
|
+
.pending_id = 101,
|
|
1936
|
+
.ledger = 60,
|
|
1937
|
+
.code = 70,
|
|
1938
|
+
.flags = .{
|
|
1939
|
+
.void_pending_transfer = true,
|
|
1940
|
+
},
|
|
1941
|
+
.amount = 80,
|
|
1267
1942
|
.timestamp = timestamp + 1,
|
|
1268
|
-
.flags = .{ .reject = true },
|
|
1269
1943
|
}),
|
|
1270
1944
|
},
|
|
1271
|
-
|
|
1272
|
-
.result = .
|
|
1273
|
-
.object =
|
|
1274
|
-
.id =
|
|
1945
|
+
.{
|
|
1946
|
+
.result = .pending_transfer_not_found,
|
|
1947
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1948
|
+
.id = 101,
|
|
1949
|
+
.debit_account_id = 10,
|
|
1950
|
+
.credit_account_id = 20,
|
|
1951
|
+
.user_data = 30,
|
|
1952
|
+
.pending_id = 102,
|
|
1953
|
+
.ledger = 60,
|
|
1954
|
+
.code = 70,
|
|
1955
|
+
.flags = .{
|
|
1956
|
+
.void_pending_transfer = true,
|
|
1957
|
+
},
|
|
1958
|
+
.amount = 80,
|
|
1275
1959
|
.timestamp = timestamp + 1,
|
|
1276
1960
|
}),
|
|
1277
1961
|
},
|
|
1278
|
-
|
|
1279
|
-
.result = .
|
|
1280
|
-
.object =
|
|
1281
|
-
.id =
|
|
1962
|
+
.{
|
|
1963
|
+
.result = .pending_transfer_not_pending,
|
|
1964
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1965
|
+
.id = 101,
|
|
1966
|
+
.debit_account_id = 10,
|
|
1967
|
+
.credit_account_id = 20,
|
|
1968
|
+
.user_data = 30,
|
|
1969
|
+
.pending_id = 1,
|
|
1970
|
+
.ledger = 60,
|
|
1971
|
+
.code = 70,
|
|
1972
|
+
.flags = .{
|
|
1973
|
+
.void_pending_transfer = true,
|
|
1974
|
+
},
|
|
1975
|
+
.amount = 80,
|
|
1282
1976
|
.timestamp = timestamp + 1,
|
|
1283
|
-
.flags = .{ .reject = true },
|
|
1284
1977
|
}),
|
|
1285
1978
|
},
|
|
1286
|
-
|
|
1287
|
-
.result = .
|
|
1288
|
-
.object =
|
|
1289
|
-
.id =
|
|
1290
|
-
.
|
|
1979
|
+
.{
|
|
1980
|
+
.result = .pending_transfer_has_different_debit_account_id,
|
|
1981
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1982
|
+
.id = 101,
|
|
1983
|
+
.debit_account_id = 10,
|
|
1984
|
+
.credit_account_id = 20,
|
|
1985
|
+
.user_data = 30,
|
|
1986
|
+
.pending_id = 2,
|
|
1987
|
+
.ledger = 60,
|
|
1988
|
+
.code = 70,
|
|
1989
|
+
.flags = .{
|
|
1990
|
+
.void_pending_transfer = true,
|
|
1991
|
+
},
|
|
1992
|
+
.amount = 80,
|
|
1993
|
+
.timestamp = timestamp + 1,
|
|
1291
1994
|
}),
|
|
1292
1995
|
},
|
|
1293
|
-
|
|
1294
|
-
.result = .
|
|
1295
|
-
.object =
|
|
1296
|
-
.id =
|
|
1297
|
-
.
|
|
1996
|
+
.{
|
|
1997
|
+
.result = .pending_transfer_has_different_credit_account_id,
|
|
1998
|
+
.object = mem.zeroInit(Transfer, .{
|
|
1999
|
+
.id = 101,
|
|
2000
|
+
.debit_account_id = 1,
|
|
2001
|
+
.credit_account_id = 20,
|
|
2002
|
+
.user_data = 30,
|
|
2003
|
+
.pending_id = 2,
|
|
2004
|
+
.ledger = 60,
|
|
2005
|
+
.code = 70,
|
|
2006
|
+
.flags = .{
|
|
2007
|
+
.void_pending_transfer = true,
|
|
2008
|
+
},
|
|
2009
|
+
.amount = 80,
|
|
2010
|
+
.timestamp = timestamp + 1,
|
|
2011
|
+
}),
|
|
2012
|
+
},
|
|
2013
|
+
.{
|
|
2014
|
+
.result = .pending_transfer_has_different_ledger,
|
|
2015
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2016
|
+
.id = 101,
|
|
2017
|
+
.debit_account_id = 1,
|
|
2018
|
+
.credit_account_id = 2,
|
|
2019
|
+
.user_data = 30,
|
|
2020
|
+
.pending_id = 2,
|
|
2021
|
+
.ledger = 60,
|
|
2022
|
+
.code = 70,
|
|
2023
|
+
.flags = .{
|
|
2024
|
+
.void_pending_transfer = true,
|
|
2025
|
+
},
|
|
2026
|
+
.amount = 80,
|
|
2027
|
+
.timestamp = timestamp + 1,
|
|
2028
|
+
}),
|
|
2029
|
+
},
|
|
2030
|
+
.{
|
|
2031
|
+
.result = .pending_transfer_has_different_code,
|
|
2032
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2033
|
+
.id = 101,
|
|
2034
|
+
.debit_account_id = 1,
|
|
2035
|
+
.credit_account_id = 2,
|
|
2036
|
+
.user_data = 30,
|
|
2037
|
+
.pending_id = 2,
|
|
2038
|
+
.ledger = 1,
|
|
2039
|
+
.code = 70,
|
|
2040
|
+
.flags = .{
|
|
2041
|
+
.void_pending_transfer = true,
|
|
2042
|
+
},
|
|
2043
|
+
.amount = 80,
|
|
2044
|
+
.timestamp = timestamp + 1,
|
|
2045
|
+
}),
|
|
2046
|
+
},
|
|
2047
|
+
.{
|
|
2048
|
+
.result = .exceeds_pending_transfer_amount,
|
|
2049
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2050
|
+
.id = 101,
|
|
2051
|
+
.debit_account_id = 1,
|
|
2052
|
+
.credit_account_id = 2,
|
|
2053
|
+
.user_data = 7000,
|
|
2054
|
+
.pending_id = 2,
|
|
2055
|
+
.ledger = 1,
|
|
2056
|
+
.code = 1,
|
|
2057
|
+
.flags = .{
|
|
2058
|
+
.void_pending_transfer = true,
|
|
2059
|
+
},
|
|
2060
|
+
.amount = 80,
|
|
2061
|
+
.timestamp = timestamp + 1,
|
|
2062
|
+
}),
|
|
2063
|
+
},
|
|
2064
|
+
.{
|
|
2065
|
+
.result = .pending_transfer_has_different_amount,
|
|
2066
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2067
|
+
.id = 101,
|
|
2068
|
+
.debit_account_id = 1,
|
|
2069
|
+
.credit_account_id = 2,
|
|
2070
|
+
.user_data = 7000,
|
|
2071
|
+
.pending_id = 2,
|
|
2072
|
+
.ledger = 1,
|
|
2073
|
+
.code = 1,
|
|
2074
|
+
.flags = .{
|
|
2075
|
+
.void_pending_transfer = true,
|
|
2076
|
+
},
|
|
2077
|
+
.amount = 1,
|
|
2078
|
+
.timestamp = timestamp + 1,
|
|
2079
|
+
}),
|
|
2080
|
+
},
|
|
2081
|
+
.{
|
|
2082
|
+
.result = .exists_with_different_flags,
|
|
2083
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2084
|
+
.id = 101,
|
|
2085
|
+
.debit_account_id = 0,
|
|
2086
|
+
.credit_account_id = 0,
|
|
2087
|
+
.user_data = 7000,
|
|
2088
|
+
.pending_id = 3,
|
|
2089
|
+
.ledger = 0,
|
|
2090
|
+
.code = 0,
|
|
2091
|
+
.flags = .{
|
|
2092
|
+
.void_pending_transfer = true,
|
|
2093
|
+
},
|
|
2094
|
+
.amount = 15,
|
|
2095
|
+
.timestamp = timestamp + 1,
|
|
1298
2096
|
}),
|
|
1299
2097
|
},
|
|
1300
|
-
|
|
1301
|
-
.result = .
|
|
1302
|
-
.object =
|
|
1303
|
-
.id =
|
|
2098
|
+
.{
|
|
2099
|
+
.result = .exists_with_different_pending_id,
|
|
2100
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2101
|
+
.id = 101,
|
|
2102
|
+
.debit_account_id = 1,
|
|
2103
|
+
.credit_account_id = 2,
|
|
2104
|
+
.user_data = 7000,
|
|
2105
|
+
.pending_id = 3,
|
|
2106
|
+
.ledger = 1,
|
|
2107
|
+
.code = 1,
|
|
2108
|
+
.flags = .{
|
|
2109
|
+
.post_pending_transfer = true,
|
|
2110
|
+
},
|
|
2111
|
+
.amount = 14,
|
|
2112
|
+
.timestamp = timestamp + 1,
|
|
2113
|
+
}),
|
|
2114
|
+
},
|
|
2115
|
+
.{
|
|
2116
|
+
.result = .exists_with_different_user_data,
|
|
2117
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2118
|
+
.id = 101,
|
|
2119
|
+
.debit_account_id = 1,
|
|
2120
|
+
.credit_account_id = 2,
|
|
2121
|
+
.user_data = 7000,
|
|
2122
|
+
.pending_id = 2,
|
|
2123
|
+
.ledger = 1,
|
|
2124
|
+
.code = 1,
|
|
2125
|
+
.flags = .{
|
|
2126
|
+
.post_pending_transfer = true,
|
|
2127
|
+
},
|
|
2128
|
+
.amount = 14,
|
|
2129
|
+
.timestamp = timestamp + 1,
|
|
2130
|
+
}),
|
|
2131
|
+
},
|
|
2132
|
+
.{
|
|
2133
|
+
.result = .exists_with_different_user_data,
|
|
2134
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2135
|
+
.id = 101,
|
|
2136
|
+
.debit_account_id = 1,
|
|
2137
|
+
.credit_account_id = 2,
|
|
2138
|
+
.user_data = 0,
|
|
2139
|
+
.pending_id = 2,
|
|
2140
|
+
.ledger = 1,
|
|
2141
|
+
.code = 1,
|
|
2142
|
+
.flags = .{
|
|
2143
|
+
.post_pending_transfer = true,
|
|
2144
|
+
},
|
|
2145
|
+
.amount = 14,
|
|
2146
|
+
.timestamp = timestamp + 1,
|
|
2147
|
+
}),
|
|
2148
|
+
},
|
|
2149
|
+
.{
|
|
2150
|
+
.result = .exists_with_different_amount,
|
|
2151
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2152
|
+
.id = 101,
|
|
2153
|
+
.debit_account_id = 1,
|
|
2154
|
+
.credit_account_id = 2,
|
|
2155
|
+
.user_data = 1000,
|
|
2156
|
+
.pending_id = 2,
|
|
2157
|
+
.ledger = 1,
|
|
2158
|
+
.code = 1,
|
|
2159
|
+
.flags = .{
|
|
2160
|
+
.post_pending_transfer = true,
|
|
2161
|
+
},
|
|
2162
|
+
.amount = 14,
|
|
2163
|
+
.timestamp = timestamp + 1,
|
|
2164
|
+
}),
|
|
2165
|
+
},
|
|
2166
|
+
.{
|
|
2167
|
+
.result = .exists_with_different_amount,
|
|
2168
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2169
|
+
.id = 101,
|
|
2170
|
+
.debit_account_id = 1,
|
|
2171
|
+
.credit_account_id = 2,
|
|
2172
|
+
.user_data = 1000,
|
|
2173
|
+
.pending_id = 2,
|
|
2174
|
+
.ledger = 1,
|
|
2175
|
+
.code = 1,
|
|
2176
|
+
.flags = .{
|
|
2177
|
+
.post_pending_transfer = true,
|
|
2178
|
+
},
|
|
2179
|
+
.amount = 0,
|
|
2180
|
+
.timestamp = timestamp + 1,
|
|
2181
|
+
}),
|
|
2182
|
+
},
|
|
2183
|
+
.{
|
|
2184
|
+
.result = .exists,
|
|
2185
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2186
|
+
.id = 101,
|
|
2187
|
+
.debit_account_id = 1,
|
|
2188
|
+
.credit_account_id = 2,
|
|
2189
|
+
.user_data = 1000,
|
|
2190
|
+
.pending_id = 2,
|
|
2191
|
+
.ledger = 1,
|
|
2192
|
+
.code = 1,
|
|
2193
|
+
.flags = .{
|
|
2194
|
+
.post_pending_transfer = true,
|
|
2195
|
+
},
|
|
2196
|
+
.amount = 13,
|
|
2197
|
+
.timestamp = timestamp + 1,
|
|
2198
|
+
}),
|
|
2199
|
+
},
|
|
2200
|
+
.{
|
|
2201
|
+
.result = .pending_transfer_already_posted,
|
|
2202
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2203
|
+
.id = 102,
|
|
2204
|
+
.debit_account_id = 1,
|
|
2205
|
+
.credit_account_id = 2,
|
|
2206
|
+
.user_data = 1000,
|
|
2207
|
+
.pending_id = 2,
|
|
2208
|
+
.ledger = 1,
|
|
2209
|
+
.code = 1,
|
|
2210
|
+
.flags = .{
|
|
2211
|
+
.post_pending_transfer = true,
|
|
2212
|
+
},
|
|
2213
|
+
.amount = 13,
|
|
2214
|
+
.timestamp = timestamp + 1,
|
|
2215
|
+
}),
|
|
2216
|
+
},
|
|
2217
|
+
.{
|
|
2218
|
+
.result = .ok,
|
|
2219
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2220
|
+
.id = 103,
|
|
2221
|
+
.debit_account_id = 0,
|
|
2222
|
+
.credit_account_id = 0,
|
|
2223
|
+
.user_data = 0,
|
|
2224
|
+
.pending_id = 3,
|
|
2225
|
+
.ledger = 0,
|
|
2226
|
+
.code = 0,
|
|
2227
|
+
.flags = .{ .void_pending_transfer = true },
|
|
2228
|
+
.amount = 15,
|
|
2229
|
+
.timestamp = timestamp + 1,
|
|
2230
|
+
}),
|
|
2231
|
+
},
|
|
2232
|
+
.{
|
|
2233
|
+
.result = .pending_transfer_already_voided,
|
|
2234
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2235
|
+
.id = 102,
|
|
2236
|
+
.debit_account_id = 1,
|
|
2237
|
+
.credit_account_id = 2,
|
|
2238
|
+
.user_data = 1000,
|
|
2239
|
+
.pending_id = 3,
|
|
2240
|
+
.ledger = 1,
|
|
2241
|
+
.code = 1,
|
|
2242
|
+
.flags = .{
|
|
2243
|
+
.post_pending_transfer = true,
|
|
2244
|
+
},
|
|
2245
|
+
.amount = 13,
|
|
1304
2246
|
.timestamp = timestamp + 2,
|
|
1305
2247
|
}),
|
|
1306
2248
|
},
|
|
1307
|
-
|
|
1308
|
-
.result = .
|
|
1309
|
-
.object =
|
|
1310
|
-
.id =
|
|
2249
|
+
.{
|
|
2250
|
+
.result = .pending_transfer_expired,
|
|
2251
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2252
|
+
.id = 102,
|
|
2253
|
+
.debit_account_id = 1,
|
|
2254
|
+
.credit_account_id = 2,
|
|
2255
|
+
.user_data = 4000,
|
|
2256
|
+
.pending_id = 4,
|
|
2257
|
+
.ledger = 1,
|
|
2258
|
+
.code = 1,
|
|
2259
|
+
.flags = .{
|
|
2260
|
+
.void_pending_transfer = true,
|
|
2261
|
+
},
|
|
2262
|
+
.amount = 15,
|
|
1311
2263
|
.timestamp = timestamp + 2,
|
|
1312
|
-
.flags = .{ .preimage = true },
|
|
1313
|
-
.reserved = [_]u8{1} ** 32,
|
|
1314
2264
|
}),
|
|
1315
2265
|
},
|
|
1316
|
-
|
|
1317
|
-
.result = .
|
|
1318
|
-
.object =
|
|
1319
|
-
.id =
|
|
2266
|
+
.{
|
|
2267
|
+
.result = .ok,
|
|
2268
|
+
.object = mem.zeroInit(Transfer, .{
|
|
2269
|
+
.id = 105,
|
|
2270
|
+
.debit_account_id = 0,
|
|
2271
|
+
.credit_account_id = 0,
|
|
2272
|
+
.user_data = 0,
|
|
2273
|
+
.pending_id = 5,
|
|
2274
|
+
.ledger = 0,
|
|
2275
|
+
.code = 0,
|
|
2276
|
+
.flags = .{ .post_pending_transfer = true },
|
|
2277
|
+
.amount = 0,
|
|
1320
2278
|
.timestamp = timestamp + 2,
|
|
1321
|
-
.flags = .{ .preimage = true },
|
|
1322
2279
|
}),
|
|
1323
2280
|
},
|
|
1324
2281
|
};
|
|
1325
2282
|
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
// Account 2:
|
|
1334
|
-
const account_2_before = state_machine.get_account(2).?.*;
|
|
1335
|
-
try testing.expectEqual(@as(u64, 0), account_2_before.debits_accepted);
|
|
1336
|
-
try testing.expectEqual(@as(u64, 0), account_2_before.debits_reserved);
|
|
1337
|
-
try testing.expectEqual(@as(u64, 15), account_2_before.credits_accepted);
|
|
1338
|
-
try testing.expectEqual(@as(u64, 75), account_2_before.credits_reserved);
|
|
1339
|
-
|
|
1340
|
-
for (vectors) |vector| {
|
|
1341
|
-
try testing.expectEqual(vector.result, state_machine.commit_transfer(vector.object));
|
|
2283
|
+
for (vectors) |vector, i| {
|
|
2284
|
+
const result = state_machine.create_transfer(&vector.object);
|
|
2285
|
+
expectEqual(vector.result, result) catch |err| {
|
|
2286
|
+
print_test_vector(i, vector.result, result, vector.object, err);
|
|
2287
|
+
return err;
|
|
2288
|
+
};
|
|
2289
|
+
|
|
1342
2290
|
if (vector.result == .ok) {
|
|
1343
|
-
|
|
2291
|
+
// Test that posted values inherit from the pending or posting transfer:
|
|
2292
|
+
const pending = state_machine.get_transfer(vector.object.pending_id).?.*;
|
|
2293
|
+
const posted = state_machine.get_transfer(vector.object.id).?.*;
|
|
2294
|
+
|
|
2295
|
+
const user_data = if (vector.object.user_data == 0)
|
|
2296
|
+
pending.user_data
|
|
2297
|
+
else
|
|
2298
|
+
vector.object.user_data;
|
|
2299
|
+
|
|
2300
|
+
const amount = if (vector.object.amount == 0)
|
|
2301
|
+
pending.amount
|
|
2302
|
+
else
|
|
2303
|
+
vector.object.amount;
|
|
2304
|
+
|
|
2305
|
+
const expected = Transfer{
|
|
2306
|
+
.id = vector.object.id,
|
|
2307
|
+
.debit_account_id = pending.debit_account_id,
|
|
2308
|
+
.credit_account_id = pending.credit_account_id,
|
|
2309
|
+
.user_data = user_data,
|
|
2310
|
+
.reserved = 0,
|
|
2311
|
+
.pending_id = pending.id,
|
|
2312
|
+
.timeout = 0,
|
|
2313
|
+
.ledger = pending.ledger,
|
|
2314
|
+
.code = pending.code,
|
|
2315
|
+
.flags = vector.object.flags,
|
|
2316
|
+
.amount = amount,
|
|
2317
|
+
.timestamp = vector.object.timestamp,
|
|
2318
|
+
};
|
|
2319
|
+
expectEqual(expected, posted) catch |err| {
|
|
2320
|
+
print_test_vector(
|
|
2321
|
+
i,
|
|
2322
|
+
expected,
|
|
2323
|
+
posted,
|
|
2324
|
+
vector.object,
|
|
2325
|
+
err,
|
|
2326
|
+
);
|
|
2327
|
+
return err;
|
|
2328
|
+
};
|
|
1344
2329
|
}
|
|
1345
2330
|
}
|
|
1346
2331
|
|
|
1347
|
-
//
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
//
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
try
|
|
1355
|
-
try
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
try
|
|
1360
|
-
|
|
1361
|
-
try
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
state_machine
|
|
1367
|
-
try
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
);
|
|
1375
|
-
state_machine
|
|
1376
|
-
try
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
);
|
|
2332
|
+
// Balances after posting:
|
|
2333
|
+
try test_account_balances(&state_machine, 1, 15, 35, 0, 0);
|
|
2334
|
+
try test_account_balances(&state_machine, 2, 0, 0, 15, 35);
|
|
2335
|
+
|
|
2336
|
+
// Rollback posting transfer (different amount):
|
|
2337
|
+
assert(vectors[0].result == .ok);
|
|
2338
|
+
try test_transfer_rollback(&state_machine, &vectors[0].object);
|
|
2339
|
+
try test_account_balances(&state_machine, 1, 30, 22, 0, 0);
|
|
2340
|
+
try test_account_balances(&state_machine, 2, 0, 0, 30, 22);
|
|
2341
|
+
|
|
2342
|
+
// Rollback voiding transfer:
|
|
2343
|
+
assert(vectors[22].result == .ok);
|
|
2344
|
+
try test_transfer_rollback(&state_machine, &vectors[22].object);
|
|
2345
|
+
try test_account_balances(&state_machine, 1, 45, 22, 0, 0);
|
|
2346
|
+
try test_account_balances(&state_machine, 2, 0, 0, 45, 22);
|
|
2347
|
+
|
|
2348
|
+
// Rollback posting transfer (zero amount):
|
|
2349
|
+
assert(vectors[25].result == .ok);
|
|
2350
|
+
try test_transfer_rollback(&state_machine, &vectors[25].object);
|
|
2351
|
+
try test_account_balances(&state_machine, 1, 52, 15, 0, 0);
|
|
2352
|
+
try test_account_balances(&state_machine, 2, 0, 0, 52, 15);
|
|
2353
|
+
|
|
2354
|
+
// Rollback all pending transfers:
|
|
2355
|
+
try test_transfer_rollback(&state_machine, &transfers[1]);
|
|
2356
|
+
try test_account_balances(&state_machine, 1, 37, 15, 0, 0);
|
|
2357
|
+
try test_account_balances(&state_machine, 2, 0, 0, 37, 15);
|
|
2358
|
+
|
|
2359
|
+
try test_transfer_rollback(&state_machine, &transfers[2]);
|
|
2360
|
+
try test_account_balances(&state_machine, 1, 22, 15, 0, 0);
|
|
2361
|
+
try test_account_balances(&state_machine, 2, 0, 0, 22, 15);
|
|
2362
|
+
|
|
2363
|
+
try test_transfer_rollback(&state_machine, &transfers[3]);
|
|
2364
|
+
try test_account_balances(&state_machine, 1, 7, 15, 0, 0);
|
|
2365
|
+
try test_account_balances(&state_machine, 2, 0, 0, 7, 15);
|
|
2366
|
+
|
|
2367
|
+
try test_transfer_rollback(&state_machine, &transfers[4]);
|
|
2368
|
+
try test_account_balances(&state_machine, 1, 0, 15, 0, 0);
|
|
2369
|
+
try test_account_balances(&state_machine, 2, 0, 0, 0, 15);
|
|
2370
|
+
|
|
2371
|
+
// Rollback transfer:
|
|
2372
|
+
try test_transfer_rollback(&state_machine, &transfers[0]);
|
|
2373
|
+
try test_account_balances(&state_machine, 1, 0, 0, 0, 0);
|
|
2374
|
+
try test_account_balances(&state_machine, 2, 0, 0, 0, 0);
|
|
2375
|
+
}
|
|
1384
2376
|
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
// -15 (rollback):
|
|
1400
|
-
try testing.expectEqual(@as(u64, 15), account_2_rollback.credits_accepted);
|
|
1401
|
-
try testing.expectEqual(@as(u64, 60), account_2_rollback.credits_reserved);
|
|
1402
|
-
|
|
1403
|
-
// Rollback [id=3] rejected:
|
|
1404
|
-
state_machine.commit_transfer_rollback(vectors[7].object);
|
|
1405
|
-
// Account 1:
|
|
1406
|
-
const account_1_rollback_reject = state_machine.get_account(1).?.*;
|
|
1407
|
-
try testing.expectEqual(@as(u64, 15), account_1_rollback_reject.debits_accepted);
|
|
1408
|
-
// Remains unchanged:
|
|
1409
|
-
try testing.expectEqual(@as(u64, 75), account_1_rollback_reject.debits_reserved);
|
|
1410
|
-
// +15 rolled back:
|
|
1411
|
-
try testing.expectEqual(@as(u64, 0), account_1_rollback_reject.credits_accepted);
|
|
1412
|
-
try testing.expectEqual(@as(u64, 0), account_1_rollback_reject.credits_reserved);
|
|
1413
|
-
// Account 2:
|
|
1414
|
-
const account_2_rollback_reject = state_machine.get_account(2).?.*;
|
|
1415
|
-
try testing.expectEqual(@as(u64, 0), account_2_rollback_reject.debits_accepted);
|
|
1416
|
-
try testing.expectEqual(@as(u64, 0), account_2_rollback_reject.debits_reserved);
|
|
1417
|
-
try testing.expectEqual(@as(u64, 15), account_2_rollback_reject.credits_accepted);
|
|
1418
|
-
// +15 rolled back"
|
|
1419
|
-
try testing.expectEqual(@as(u64, 75), account_2_rollback_reject.credits_reserved);
|
|
2377
|
+
fn print_test_vector(
|
|
2378
|
+
i: usize,
|
|
2379
|
+
result_expect: anytype,
|
|
2380
|
+
result_actual: anytype,
|
|
2381
|
+
vector_object: anytype,
|
|
2382
|
+
err: anyerror,
|
|
2383
|
+
) void {
|
|
2384
|
+
std.debug.print("\nindex={}\n\nexpect: {}\n\nactual: {}\n\nobject: {}\n\nerr={}", .{
|
|
2385
|
+
i,
|
|
2386
|
+
result_expect,
|
|
2387
|
+
result_actual,
|
|
2388
|
+
vector_object,
|
|
2389
|
+
err,
|
|
2390
|
+
});
|
|
1420
2391
|
}
|
|
1421
2392
|
|
|
1422
|
-
fn
|
|
1423
|
-
const
|
|
2393
|
+
fn test_account_balances(
|
|
2394
|
+
state_machine: *const StateMachine,
|
|
2395
|
+
account_id: u128,
|
|
2396
|
+
debits_pending: u64,
|
|
2397
|
+
debits_posted: u64,
|
|
2398
|
+
credits_pending: u64,
|
|
2399
|
+
credits_posted: u64,
|
|
2400
|
+
) !void {
|
|
2401
|
+
const account = state_machine.get_account(account_id).?.*;
|
|
2402
|
+
try expectEqual(debits_pending, account.debits_pending);
|
|
2403
|
+
try expectEqual(debits_posted, account.debits_posted);
|
|
2404
|
+
try expectEqual(credits_pending, account.credits_pending);
|
|
2405
|
+
try expectEqual(credits_posted, account.credits_posted);
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
fn test_transfer_rollback(state_machine: *StateMachine, transfer: *const Transfer) !void {
|
|
2409
|
+
assert(state_machine.get_transfer(transfer.id) != null);
|
|
2410
|
+
|
|
2411
|
+
state_machine.create_transfer_rollback(transfer);
|
|
2412
|
+
|
|
2413
|
+
try expect(state_machine.get_transfer(transfer.id) == null);
|
|
2414
|
+
if (transfer.flags.post_pending_transfer or transfer.flags.void_pending_transfer) {
|
|
2415
|
+
try expect(state_machine.get_posted(transfer.pending_id) == null);
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
|
|
2419
|
+
test "zeroed_32_bytes" {
|
|
2420
|
+
try test_zeroed_n_bytes(32);
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
test "zeroed_48_bytes" {
|
|
2424
|
+
try test_zeroed_n_bytes(48);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
fn test_zeroed_n_bytes(comptime n: usize) !void {
|
|
2428
|
+
const routine = switch (n) {
|
|
1424
2429
|
32 => zeroed_32_bytes,
|
|
1425
2430
|
48 => zeroed_48_bytes,
|
|
1426
2431
|
else => unreachable,
|
|
1427
2432
|
};
|
|
1428
|
-
var a = [_]u8{0} **
|
|
2433
|
+
var a = [_]u8{0} ** n;
|
|
1429
2434
|
var i: usize = 0;
|
|
1430
2435
|
while (i < a.len) : (i += 1) {
|
|
1431
2436
|
a[i] = 1;
|
|
1432
|
-
try
|
|
2437
|
+
try expectEqual(false, routine(a));
|
|
1433
2438
|
a[i] = 0;
|
|
1434
2439
|
}
|
|
1435
|
-
try
|
|
2440
|
+
try expectEqual(true, routine(a));
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
test "equal_32_bytes" {
|
|
2444
|
+
try test_equal_n_bytes(32);
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
test "equal_48_bytes" {
|
|
2448
|
+
try test_equal_n_bytes(48);
|
|
1436
2449
|
}
|
|
1437
2450
|
|
|
1438
|
-
fn
|
|
1439
|
-
const routine = switch (
|
|
2451
|
+
fn test_equal_n_bytes(comptime n: usize) !void {
|
|
2452
|
+
const routine = switch (n) {
|
|
1440
2453
|
32 => equal_32_bytes,
|
|
1441
2454
|
48 => equal_48_bytes,
|
|
1442
2455
|
else => unreachable,
|
|
1443
2456
|
};
|
|
1444
|
-
var a = [_]u8{0} **
|
|
1445
|
-
var b = [_]u8{0} **
|
|
2457
|
+
var a = [_]u8{0} ** n;
|
|
2458
|
+
var b = [_]u8{0} ** n;
|
|
1446
2459
|
var i: usize = 0;
|
|
1447
2460
|
while (i < a.len) : (i += 1) {
|
|
1448
2461
|
a[i] = 1;
|
|
1449
|
-
try
|
|
2462
|
+
try expectEqual(false, routine(a, b));
|
|
1450
2463
|
a[i] = 0;
|
|
1451
2464
|
|
|
1452
2465
|
b[i] = 1;
|
|
1453
|
-
try
|
|
2466
|
+
try expectEqual(false, routine(a, b));
|
|
1454
2467
|
b[i] = 0;
|
|
1455
2468
|
}
|
|
1456
|
-
try
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
test "zeroed_32_bytes" {
|
|
1460
|
-
try test_routine_zeroed(32);
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
test "zeroed_48_bytes" {
|
|
1464
|
-
try test_routine_zeroed(48);
|
|
1465
|
-
}
|
|
1466
|
-
|
|
1467
|
-
test "equal_32_bytes" {
|
|
1468
|
-
try test_routine_equal(32);
|
|
1469
|
-
}
|
|
1470
|
-
|
|
1471
|
-
test "equal_48_bytes" {
|
|
1472
|
-
try test_routine_equal(48);
|
|
2469
|
+
try expectEqual(true, routine(a, b));
|
|
1473
2470
|
}
|