tigerbeetle-node 0.3.3 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -7
- package/dist/benchmark.js +1 -1
- package/dist/benchmark.js.map +1 -1
- package/dist/index.d.ts +22 -20
- package/dist/index.js +40 -18
- package/dist/index.js.map +1 -1
- package/dist/test.js +13 -1
- package/dist/test.js.map +1 -1
- package/package.json +12 -12
- package/scripts/postinstall.sh +2 -2
- package/src/benchmark.ts +4 -4
- package/src/index.ts +35 -9
- package/src/node.zig +139 -28
- package/src/test.ts +19 -5
- package/src/tigerbeetle/scripts/benchmark.sh +10 -3
- package/src/tigerbeetle/scripts/install.sh +2 -2
- package/src/tigerbeetle/scripts/install_zig.bat +109 -0
- package/src/tigerbeetle/scripts/install_zig.sh +21 -4
- package/src/tigerbeetle/scripts/vopr.bat +48 -0
- package/src/tigerbeetle/scripts/vopr.sh +33 -0
- package/src/tigerbeetle/src/benchmark.zig +74 -42
- package/src/tigerbeetle/src/cli.zig +136 -83
- package/src/tigerbeetle/src/config.zig +80 -26
- package/src/tigerbeetle/src/demo.zig +101 -78
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +2 -7
- package/src/tigerbeetle/src/demo_02_lookup_accounts.zig +2 -7
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +2 -7
- package/src/tigerbeetle/src/demo_04_create_transfers_two_phase_commit.zig +2 -5
- package/src/tigerbeetle/src/demo_05_accept_transfers.zig +2 -7
- package/src/tigerbeetle/src/demo_06_reject_transfers.zig +2 -7
- package/src/tigerbeetle/src/demo_07_lookup_transfers.zig +8 -0
- package/src/tigerbeetle/src/fifo.zig +20 -11
- package/src/tigerbeetle/src/io.zig +35 -22
- package/src/tigerbeetle/src/io_darwin.zig +701 -0
- package/src/tigerbeetle/src/main.zig +72 -25
- package/src/tigerbeetle/src/message_bus.zig +379 -456
- package/src/tigerbeetle/src/message_pool.zig +3 -3
- package/src/tigerbeetle/src/ring_buffer.zig +192 -37
- package/src/tigerbeetle/src/simulator.zig +317 -0
- package/src/tigerbeetle/src/state_machine.zig +846 -38
- package/src/tigerbeetle/src/storage.zig +488 -90
- package/src/tigerbeetle/src/test/cluster.zig +221 -0
- package/src/tigerbeetle/src/test/message_bus.zig +92 -0
- package/src/tigerbeetle/src/test/network.zig +182 -0
- package/src/tigerbeetle/src/test/packet_simulator.zig +371 -0
- package/src/tigerbeetle/src/test/state_checker.zig +142 -0
- package/src/tigerbeetle/src/test/state_machine.zig +71 -0
- package/src/tigerbeetle/src/test/storage.zig +375 -0
- package/src/tigerbeetle/src/test/time.zig +84 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +6 -3
- package/src/tigerbeetle/src/time.zig +65 -0
- package/src/tigerbeetle/src/unit_tests.zig +14 -0
- package/src/tigerbeetle/src/vsr/client.zig +519 -0
- package/src/tigerbeetle/src/vsr/clock.zig +829 -0
- package/src/tigerbeetle/src/vsr/journal.zig +1368 -0
- package/src/tigerbeetle/src/vsr/marzullo.zig +306 -0
- package/src/tigerbeetle/src/vsr/replica.zig +4248 -0
- package/src/tigerbeetle/src/vsr.zig +601 -0
- package/src/tigerbeetle/LICENSE +0 -177
- package/src/tigerbeetle/README.md +0 -116
- package/src/tigerbeetle/src/client.zig +0 -319
- package/src/tigerbeetle/src/concurrent_ranges.zig +0 -162
- package/src/tigerbeetle/src/fixed_array_list.zig +0 -53
- package/src/tigerbeetle/src/io_async.zig +0 -600
- package/src/tigerbeetle/src/journal.zig +0 -567
- package/src/tigerbeetle/src/test_client.zig +0 -41
- package/src/tigerbeetle/src/test_main.zig +0 -118
- package/src/tigerbeetle/src/test_message_bus.zig +0 -132
- package/src/tigerbeetle/src/vr/journal.zig +0 -672
- package/src/tigerbeetle/src/vr/replica.zig +0 -3061
- package/src/tigerbeetle/src/vr.zig +0 -374
|
@@ -8,22 +8,25 @@ const HashMapAccounts = std.AutoHashMap(u128, Account);
|
|
|
8
8
|
const HashMapTransfers = std.AutoHashMap(u128, Transfer);
|
|
9
9
|
const HashMapCommits = std.AutoHashMap(u128, Commit);
|
|
10
10
|
|
|
11
|
-
pub const Operation = packed enum(u8) {
|
|
12
|
-
// We reserve command "0" to detect an accidental zero byte being interpreted as an operation:
|
|
13
|
-
reserved,
|
|
14
|
-
init,
|
|
15
|
-
|
|
16
|
-
create_accounts,
|
|
17
|
-
create_transfers,
|
|
18
|
-
commit_transfers,
|
|
19
|
-
lookup_accounts,
|
|
20
|
-
|
|
21
|
-
pub fn jsonStringify(self: Command, options: StringifyOptions, writer: anytype) !void {
|
|
22
|
-
try std.fmt.format(writer, "\"{}\"", .{@tagName(self)});
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
11
|
pub const StateMachine = struct {
|
|
12
|
+
pub const Operation = enum(u8) {
|
|
13
|
+
/// Operations reserved by VR protocol (for all state machines):
|
|
14
|
+
reserved,
|
|
15
|
+
init,
|
|
16
|
+
register,
|
|
17
|
+
|
|
18
|
+
/// Operations exported by TigerBeetle:
|
|
19
|
+
create_accounts,
|
|
20
|
+
create_transfers,
|
|
21
|
+
commit_transfers,
|
|
22
|
+
lookup_accounts,
|
|
23
|
+
lookup_transfers,
|
|
24
|
+
|
|
25
|
+
pub fn jsonStringify(self: Command, options: StringifyOptions, writer: anytype) !void {
|
|
26
|
+
try std.fmt.format(writer, "\"{}\"", .{@tagName(self)});
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
27
30
|
allocator: *std.mem.Allocator,
|
|
28
31
|
prepare_timestamp: u64,
|
|
29
32
|
commit_timestamp: u64,
|
|
@@ -74,6 +77,7 @@ pub const StateMachine = struct {
|
|
|
74
77
|
.create_transfers => Transfer,
|
|
75
78
|
.commit_transfers => Commit,
|
|
76
79
|
.lookup_accounts => u128,
|
|
80
|
+
.lookup_transfers => u128,
|
|
77
81
|
else => unreachable,
|
|
78
82
|
};
|
|
79
83
|
}
|
|
@@ -84,25 +88,36 @@ pub const StateMachine = struct {
|
|
|
84
88
|
.create_transfers => CreateTransfersResult,
|
|
85
89
|
.commit_transfers => CommitTransfersResult,
|
|
86
90
|
.lookup_accounts => Account,
|
|
91
|
+
.lookup_transfers => Transfer,
|
|
87
92
|
else => unreachable,
|
|
88
93
|
};
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
pub fn prepare(self: *StateMachine, operation: Operation, input: []u8) void {
|
|
96
|
+
pub fn prepare(self: *StateMachine, realtime: i64, operation: Operation, input: []u8) void {
|
|
92
97
|
switch (operation) {
|
|
93
|
-
.
|
|
94
|
-
.
|
|
95
|
-
.
|
|
98
|
+
.init => unreachable,
|
|
99
|
+
.register => {},
|
|
100
|
+
.create_accounts => self.prepare_timestamps(realtime, .create_accounts, input),
|
|
101
|
+
.create_transfers => self.prepare_timestamps(realtime, .create_transfers, input),
|
|
102
|
+
.commit_transfers => self.prepare_timestamps(realtime, .commit_transfers, input),
|
|
96
103
|
.lookup_accounts => {},
|
|
104
|
+
.lookup_transfers => {},
|
|
97
105
|
else => unreachable,
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
fn prepare_timestamps(
|
|
109
|
+
fn prepare_timestamps(
|
|
110
|
+
self: *StateMachine,
|
|
111
|
+
realtime: i64,
|
|
112
|
+
comptime operation: Operation,
|
|
113
|
+
input: []u8,
|
|
114
|
+
) void {
|
|
102
115
|
// Guard against the wall clock going backwards by taking the max with timestamps issued:
|
|
103
116
|
self.prepare_timestamp = std.math.max(
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
// The cluster `commit_timestamp` may be ahead of our `prepare_timestamp` because this
|
|
118
|
+
// may be our first prepare as a recently elected leader:
|
|
119
|
+
std.math.max(self.prepare_timestamp, self.commit_timestamp) + 1,
|
|
120
|
+
@intCast(u64, realtime),
|
|
106
121
|
);
|
|
107
122
|
assert(self.prepare_timestamp > self.commit_timestamp);
|
|
108
123
|
var sum_reserved_timestamps: usize = 0;
|
|
@@ -120,16 +135,19 @@ pub const StateMachine = struct {
|
|
|
120
135
|
|
|
121
136
|
pub fn commit(
|
|
122
137
|
self: *StateMachine,
|
|
138
|
+
client: u128,
|
|
123
139
|
operation: Operation,
|
|
124
140
|
input: []const u8,
|
|
125
141
|
output: []u8,
|
|
126
142
|
) usize {
|
|
127
143
|
return switch (operation) {
|
|
128
|
-
.init =>
|
|
144
|
+
.init => unreachable,
|
|
145
|
+
.register => 0,
|
|
129
146
|
.create_accounts => self.execute(.create_accounts, input, output),
|
|
130
147
|
.create_transfers => self.execute(.create_transfers, input, output),
|
|
131
148
|
.commit_transfers => self.execute(.commit_transfers, input, output),
|
|
132
149
|
.lookup_accounts => self.execute_lookup_accounts(input, output),
|
|
150
|
+
.lookup_transfers => self.execute_lookup_transfers(input, output),
|
|
133
151
|
else => unreachable,
|
|
134
152
|
};
|
|
135
153
|
}
|
|
@@ -140,7 +158,7 @@ pub const StateMachine = struct {
|
|
|
140
158
|
input: []const u8,
|
|
141
159
|
output: []u8,
|
|
142
160
|
) usize {
|
|
143
|
-
comptime assert(operation != .lookup_accounts);
|
|
161
|
+
comptime assert(operation != .lookup_accounts and operation != .lookup_transfers);
|
|
144
162
|
|
|
145
163
|
const events = std.mem.bytesAsSlice(Event(operation), input);
|
|
146
164
|
var results = std.mem.bytesAsSlice(Result(operation), output);
|
|
@@ -242,8 +260,8 @@ pub const StateMachine = struct {
|
|
|
242
260
|
|
|
243
261
|
fn execute_lookup_accounts(self: *StateMachine, input: []const u8, output: []u8) usize {
|
|
244
262
|
const batch = std.mem.bytesAsSlice(u128, input);
|
|
245
|
-
|
|
246
|
-
|
|
263
|
+
const output_len = @divFloor(output.len, @sizeOf(Account)) * @sizeOf(Account);
|
|
264
|
+
const results = std.mem.bytesAsSlice(Account, output[0..output_len]);
|
|
247
265
|
var results_count: usize = 0;
|
|
248
266
|
for (batch) |id, index| {
|
|
249
267
|
if (self.get_account(id)) |result| {
|
|
@@ -254,6 +272,20 @@ pub const StateMachine = struct {
|
|
|
254
272
|
return results_count * @sizeOf(Account);
|
|
255
273
|
}
|
|
256
274
|
|
|
275
|
+
fn execute_lookup_transfers(self: *StateMachine, input: []const u8, output: []u8) usize {
|
|
276
|
+
const batch = std.mem.bytesAsSlice(u128, input);
|
|
277
|
+
const output_len = @divFloor(output.len, @sizeOf(Transfer)) * @sizeOf(Transfer);
|
|
278
|
+
const results = std.mem.bytesAsSlice(Transfer, output[0..output_len]);
|
|
279
|
+
var results_count: usize = 0;
|
|
280
|
+
for (batch) |id, index| {
|
|
281
|
+
if (self.get_transfer(id)) |result| {
|
|
282
|
+
results[results_count] = result.*;
|
|
283
|
+
results_count += 1;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return results_count * @sizeOf(Transfer);
|
|
287
|
+
}
|
|
288
|
+
|
|
257
289
|
fn create_account(self: *StateMachine, a: Account) CreateAccountResult {
|
|
258
290
|
assert(a.timestamp > self.commit_timestamp);
|
|
259
291
|
|
|
@@ -326,8 +358,7 @@ pub const StateMachine = struct {
|
|
|
326
358
|
const exists = insert.value_ptr.*;
|
|
327
359
|
if (exists.debit_account_id != t.debit_account_id) {
|
|
328
360
|
return .exists_with_different_debit_account_id;
|
|
329
|
-
}
|
|
330
|
-
if (exists.credit_account_id != t.credit_account_id) {
|
|
361
|
+
} else if (exists.credit_account_id != t.credit_account_id) {
|
|
331
362
|
return .exists_with_different_credit_account_id;
|
|
332
363
|
}
|
|
333
364
|
if (exists.amount != t.amount) return .exists_with_different_amount;
|
|
@@ -424,6 +455,7 @@ pub const StateMachine = struct {
|
|
|
424
455
|
}
|
|
425
456
|
|
|
426
457
|
fn commit_transfer_rollback(self: *StateMachine, c: Commit) void {
|
|
458
|
+
assert(self.get_commit(c.id) != null);
|
|
427
459
|
var t = self.get_transfer(c.id).?;
|
|
428
460
|
var dr = self.get_account(t.debit_account_id).?;
|
|
429
461
|
var cr = self.get_account(t.credit_account_id).?;
|
|
@@ -503,6 +535,161 @@ fn equal_48_bytes(a: [48]u8, b: [48]u8) bool {
|
|
|
503
535
|
|
|
504
536
|
const testing = std.testing;
|
|
505
537
|
|
|
538
|
+
test "create/lookup accounts" {
|
|
539
|
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
540
|
+
defer arena.deinit();
|
|
541
|
+
const allocator = &arena.allocator;
|
|
542
|
+
|
|
543
|
+
const Vector = struct { result: CreateAccountResult, object: Account };
|
|
544
|
+
|
|
545
|
+
const vectors = [_]Vector{
|
|
546
|
+
Vector{
|
|
547
|
+
.result = .reserved_flag_padding,
|
|
548
|
+
.object = std.mem.zeroInit(Account, .{
|
|
549
|
+
.id = 1,
|
|
550
|
+
.timestamp = 1,
|
|
551
|
+
.flags = .{ .padding = 1 },
|
|
552
|
+
}),
|
|
553
|
+
},
|
|
554
|
+
Vector{
|
|
555
|
+
.result = .reserved_field,
|
|
556
|
+
.object = std.mem.zeroInit(Account, .{
|
|
557
|
+
.id = 2,
|
|
558
|
+
.timestamp = 1,
|
|
559
|
+
.reserved = [_]u8{1} ** 48,
|
|
560
|
+
}),
|
|
561
|
+
},
|
|
562
|
+
Vector{
|
|
563
|
+
.result = .exceeds_credits,
|
|
564
|
+
.object = std.mem.zeroInit(Account, .{
|
|
565
|
+
.id = 3,
|
|
566
|
+
.timestamp = 1,
|
|
567
|
+
.debits_reserved = 10,
|
|
568
|
+
.flags = .{ .debits_must_not_exceed_credits = true },
|
|
569
|
+
}),
|
|
570
|
+
},
|
|
571
|
+
Vector{
|
|
572
|
+
.result = .exceeds_credits,
|
|
573
|
+
.object = std.mem.zeroInit(Account, .{
|
|
574
|
+
.id = 4,
|
|
575
|
+
.timestamp = 1,
|
|
576
|
+
.debits_accepted = 10,
|
|
577
|
+
.flags = .{ .debits_must_not_exceed_credits = true },
|
|
578
|
+
}),
|
|
579
|
+
},
|
|
580
|
+
Vector{
|
|
581
|
+
.result = .exceeds_debits,
|
|
582
|
+
.object = std.mem.zeroInit(Account, .{
|
|
583
|
+
.id = 5,
|
|
584
|
+
.timestamp = 1,
|
|
585
|
+
.credits_reserved = 10,
|
|
586
|
+
.flags = .{ .credits_must_not_exceed_debits = true },
|
|
587
|
+
}),
|
|
588
|
+
},
|
|
589
|
+
Vector{
|
|
590
|
+
.result = .exceeds_debits,
|
|
591
|
+
.object = std.mem.zeroInit(Account, .{
|
|
592
|
+
.id = 6,
|
|
593
|
+
.timestamp = 1,
|
|
594
|
+
.credits_accepted = 10,
|
|
595
|
+
.flags = .{ .credits_must_not_exceed_debits = true },
|
|
596
|
+
}),
|
|
597
|
+
},
|
|
598
|
+
Vector{
|
|
599
|
+
.result = .ok,
|
|
600
|
+
.object = std.mem.zeroInit(Account, .{
|
|
601
|
+
.id = 7,
|
|
602
|
+
.timestamp = 1,
|
|
603
|
+
}),
|
|
604
|
+
},
|
|
605
|
+
Vector{
|
|
606
|
+
.result = .exists,
|
|
607
|
+
.object = std.mem.zeroInit(Account, .{
|
|
608
|
+
.id = 7,
|
|
609
|
+
.timestamp = 2,
|
|
610
|
+
}),
|
|
611
|
+
},
|
|
612
|
+
Vector{
|
|
613
|
+
.result = .ok,
|
|
614
|
+
.object = std.mem.zeroInit(Account, .{
|
|
615
|
+
.id = 8,
|
|
616
|
+
.timestamp = 2,
|
|
617
|
+
.user_data = 'U',
|
|
618
|
+
.unit = 9,
|
|
619
|
+
}),
|
|
620
|
+
},
|
|
621
|
+
Vector{
|
|
622
|
+
.result = .exists_with_different_unit,
|
|
623
|
+
.object = std.mem.zeroInit(Account, .{
|
|
624
|
+
.id = 8,
|
|
625
|
+
.timestamp = 3,
|
|
626
|
+
.user_data = 'U',
|
|
627
|
+
.unit = 10,
|
|
628
|
+
}),
|
|
629
|
+
},
|
|
630
|
+
Vector{
|
|
631
|
+
.result = .ok,
|
|
632
|
+
.object = std.mem.zeroInit(Account, .{
|
|
633
|
+
.id = 9,
|
|
634
|
+
.timestamp = 3,
|
|
635
|
+
.code = 9,
|
|
636
|
+
.user_data = 'U',
|
|
637
|
+
}),
|
|
638
|
+
},
|
|
639
|
+
Vector{
|
|
640
|
+
.result = .exists_with_different_code,
|
|
641
|
+
.object = std.mem.zeroInit(Account, .{
|
|
642
|
+
.id = 9,
|
|
643
|
+
.timestamp = 4,
|
|
644
|
+
.code = 10,
|
|
645
|
+
.user_data = 'D',
|
|
646
|
+
}),
|
|
647
|
+
},
|
|
648
|
+
Vector{
|
|
649
|
+
.result = .ok,
|
|
650
|
+
.object = std.mem.zeroInit(Account, .{
|
|
651
|
+
.id = 10,
|
|
652
|
+
.timestamp = 4,
|
|
653
|
+
.flags = .{ .credits_must_not_exceed_debits = true },
|
|
654
|
+
}),
|
|
655
|
+
},
|
|
656
|
+
Vector{
|
|
657
|
+
.result = .exists_with_different_flags,
|
|
658
|
+
.object = std.mem.zeroInit(Account, .{
|
|
659
|
+
.id = 10,
|
|
660
|
+
.timestamp = 5,
|
|
661
|
+
.flags = .{ .debits_must_not_exceed_credits = true },
|
|
662
|
+
}),
|
|
663
|
+
},
|
|
664
|
+
Vector{
|
|
665
|
+
.result = .ok,
|
|
666
|
+
.object = std.mem.zeroInit(Account, .{
|
|
667
|
+
.id = 11,
|
|
668
|
+
.timestamp = 5,
|
|
669
|
+
.user_data = 'U',
|
|
670
|
+
}),
|
|
671
|
+
},
|
|
672
|
+
Vector{
|
|
673
|
+
.result = .exists_with_different_user_data,
|
|
674
|
+
.object = std.mem.zeroInit(Account, .{
|
|
675
|
+
.id = 11,
|
|
676
|
+
.timestamp = 6,
|
|
677
|
+
.user_data = 'D',
|
|
678
|
+
}),
|
|
679
|
+
},
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
var state_machine = try StateMachine.init(allocator, vectors.len, 0, 0);
|
|
683
|
+
defer state_machine.deinit();
|
|
684
|
+
|
|
685
|
+
for (vectors) |vector| {
|
|
686
|
+
try testing.expectEqual(vector.result, state_machine.create_account(vector.object));
|
|
687
|
+
if (vector.result == .ok) {
|
|
688
|
+
try testing.expectEqual(vector.object, state_machine.get_account(vector.object.id).?.*);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
|
|
506
693
|
test "linked accounts" {
|
|
507
694
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
508
695
|
defer arena.deinit();
|
|
@@ -512,21 +699,22 @@ test "linked accounts" {
|
|
|
512
699
|
const transfers_max = 0;
|
|
513
700
|
const commits_max = 0;
|
|
514
701
|
|
|
515
|
-
var state_machine = try StateMachine.init(allocator, accounts_max, transfers_max, commits_max);
|
|
516
|
-
defer state_machine.deinit();
|
|
517
|
-
|
|
518
702
|
var accounts = [_]Account{
|
|
519
703
|
// An individual event (successful):
|
|
520
704
|
std.mem.zeroInit(Account, .{ .id = 7, .code = 200 }),
|
|
521
705
|
|
|
522
706
|
// A chain of 4 events (the last event in the chain closes the chain with linked=false):
|
|
523
|
-
|
|
524
|
-
std.mem.zeroInit(Account, .{ .id =
|
|
525
|
-
|
|
526
|
-
std.mem.zeroInit(Account, .{ .id =
|
|
707
|
+
// Commit/rollback:
|
|
708
|
+
std.mem.zeroInit(Account, .{ .id = 0, .flags = .{ .linked = true } }),
|
|
709
|
+
// Commit/rollback:
|
|
710
|
+
std.mem.zeroInit(Account, .{ .id = 1, .flags = .{ .linked = true } }),
|
|
711
|
+
// Fail with .exists:
|
|
712
|
+
std.mem.zeroInit(Account, .{ .id = 0, .flags = .{ .linked = true } }),
|
|
713
|
+
// Fail without committing.
|
|
714
|
+
std.mem.zeroInit(Account, .{ .id = 2 }),
|
|
527
715
|
|
|
528
716
|
// An individual event (successful):
|
|
529
|
-
// This should not see any effect from the failed chain above
|
|
717
|
+
// This should not see any effect from the failed chain above:
|
|
530
718
|
std.mem.zeroInit(Account, .{ .id = 0, .code = 200 }),
|
|
531
719
|
|
|
532
720
|
// A chain of 2 events (the first event fails the chain):
|
|
@@ -545,11 +733,14 @@ test "linked accounts" {
|
|
|
545
733
|
std.mem.zeroInit(Account, .{ .id = 3 }),
|
|
546
734
|
};
|
|
547
735
|
|
|
736
|
+
var state_machine = try StateMachine.init(allocator, accounts.len, transfers_max, commits_max);
|
|
737
|
+
defer state_machine.deinit();
|
|
738
|
+
|
|
548
739
|
const input = std.mem.asBytes(&accounts);
|
|
549
740
|
const output = try allocator.alloc(u8, 4096);
|
|
550
741
|
|
|
551
|
-
state_machine.prepare(.create_accounts, input);
|
|
552
|
-
const size = state_machine.commit(.create_accounts, input, output);
|
|
742
|
+
state_machine.prepare(0, .create_accounts, input);
|
|
743
|
+
const size = state_machine.commit(0, .create_accounts, input, output);
|
|
553
744
|
const results = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
|
|
554
745
|
|
|
555
746
|
try testing.expectEqualSlices(
|
|
@@ -580,6 +771,623 @@ test "linked accounts" {
|
|
|
580
771
|
// All our rollback handlers appear to be commutative.
|
|
581
772
|
}
|
|
582
773
|
|
|
774
|
+
test "create/lookup/rollback transfers" {
|
|
775
|
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
776
|
+
defer arena.deinit();
|
|
777
|
+
const allocator = &arena.allocator;
|
|
778
|
+
|
|
779
|
+
var accounts = [_]Account{
|
|
780
|
+
std.mem.zeroInit(Account, .{ .id = 1 }),
|
|
781
|
+
std.mem.zeroInit(Account, .{ .id = 2 }),
|
|
782
|
+
std.mem.zeroInit(Account, .{ .id = 3, .unit = 1 }),
|
|
783
|
+
std.mem.zeroInit(Account, .{ .id = 4, .unit = 2 }),
|
|
784
|
+
std.mem.zeroInit(Account, .{ .id = 5, .flags = .{ .debits_must_not_exceed_credits = true } }),
|
|
785
|
+
std.mem.zeroInit(Account, .{ .id = 6, .flags = .{ .credits_must_not_exceed_debits = true } }),
|
|
786
|
+
std.mem.zeroInit(Account, .{ .id = 7 }),
|
|
787
|
+
std.mem.zeroInit(Account, .{ .id = 8 }),
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
var state_machine = try StateMachine.init(allocator, accounts.len, 1, 0);
|
|
791
|
+
defer state_machine.deinit();
|
|
792
|
+
|
|
793
|
+
const input = std.mem.asBytes(&accounts);
|
|
794
|
+
const output = try allocator.alloc(u8, 4096);
|
|
795
|
+
|
|
796
|
+
state_machine.prepare(0, .create_accounts, input);
|
|
797
|
+
const size = state_machine.commit(0, .create_accounts, input, output);
|
|
798
|
+
const results = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
|
|
799
|
+
|
|
800
|
+
for (accounts) |account, i| {
|
|
801
|
+
try testing.expectEqual(accounts[i], state_machine.get_account(accounts[i].id).?.*);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const Vector = struct { result: CreateTransferResult, object: Transfer };
|
|
805
|
+
|
|
806
|
+
const timestamp: u64 = (state_machine.commit_timestamp + 1);
|
|
807
|
+
const vectors = [_]Vector{
|
|
808
|
+
Vector{
|
|
809
|
+
.result = .amount_is_zero,
|
|
810
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
811
|
+
.id = 1,
|
|
812
|
+
.timestamp = timestamp,
|
|
813
|
+
}),
|
|
814
|
+
},
|
|
815
|
+
Vector{
|
|
816
|
+
.result = .reserved_flag_padding,
|
|
817
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
818
|
+
.id = 2,
|
|
819
|
+
.timestamp = timestamp,
|
|
820
|
+
.flags = .{ .padding = 1 },
|
|
821
|
+
}),
|
|
822
|
+
},
|
|
823
|
+
Vector{
|
|
824
|
+
.result = .two_phase_commit_must_timeout,
|
|
825
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
826
|
+
.id = 3,
|
|
827
|
+
.timestamp = timestamp,
|
|
828
|
+
.flags = .{ .two_phase_commit = true },
|
|
829
|
+
}),
|
|
830
|
+
},
|
|
831
|
+
Vector{
|
|
832
|
+
.result = .timeout_reserved_for_two_phase_commit,
|
|
833
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
834
|
+
.id = 4,
|
|
835
|
+
.timestamp = timestamp,
|
|
836
|
+
.timeout = 1,
|
|
837
|
+
}),
|
|
838
|
+
},
|
|
839
|
+
Vector{
|
|
840
|
+
.result = .reserved_field,
|
|
841
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
842
|
+
.id = 5,
|
|
843
|
+
.timestamp = timestamp,
|
|
844
|
+
.flags = .{ .condition = false },
|
|
845
|
+
.reserved = [_]u8{1} ** 32,
|
|
846
|
+
}),
|
|
847
|
+
},
|
|
848
|
+
Vector{
|
|
849
|
+
.result = .accounts_are_the_same,
|
|
850
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
851
|
+
.id = 6,
|
|
852
|
+
.timestamp = timestamp,
|
|
853
|
+
.amount = 10,
|
|
854
|
+
.debit_account_id = 1,
|
|
855
|
+
.credit_account_id = 1,
|
|
856
|
+
}),
|
|
857
|
+
},
|
|
858
|
+
Vector{
|
|
859
|
+
.result = .debit_account_not_found,
|
|
860
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
861
|
+
.id = 7,
|
|
862
|
+
.timestamp = timestamp,
|
|
863
|
+
.amount = 10,
|
|
864
|
+
.debit_account_id = 100,
|
|
865
|
+
.credit_account_id = 1,
|
|
866
|
+
}),
|
|
867
|
+
},
|
|
868
|
+
Vector{
|
|
869
|
+
.result = .credit_account_not_found,
|
|
870
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
871
|
+
.id = 8,
|
|
872
|
+
.timestamp = timestamp,
|
|
873
|
+
.amount = 10,
|
|
874
|
+
.debit_account_id = 1,
|
|
875
|
+
.credit_account_id = 100,
|
|
876
|
+
}),
|
|
877
|
+
},
|
|
878
|
+
Vector{
|
|
879
|
+
.result = .accounts_have_different_units,
|
|
880
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
881
|
+
.id = 9,
|
|
882
|
+
.timestamp = timestamp,
|
|
883
|
+
.amount = 10,
|
|
884
|
+
.debit_account_id = 3,
|
|
885
|
+
.credit_account_id = 4,
|
|
886
|
+
}),
|
|
887
|
+
},
|
|
888
|
+
Vector{
|
|
889
|
+
.result = .exceeds_credits,
|
|
890
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
891
|
+
.id = 10,
|
|
892
|
+
.timestamp = timestamp,
|
|
893
|
+
.amount = 1000,
|
|
894
|
+
.debit_account_id = 5,
|
|
895
|
+
.credit_account_id = 1,
|
|
896
|
+
}),
|
|
897
|
+
},
|
|
898
|
+
Vector{
|
|
899
|
+
.result = .exceeds_debits,
|
|
900
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
901
|
+
.id = 11,
|
|
902
|
+
.timestamp = timestamp,
|
|
903
|
+
.amount = 1000,
|
|
904
|
+
.debit_account_id = 1,
|
|
905
|
+
.credit_account_id = 6,
|
|
906
|
+
}),
|
|
907
|
+
},
|
|
908
|
+
Vector{
|
|
909
|
+
.result = .ok,
|
|
910
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
911
|
+
.id = 12,
|
|
912
|
+
.timestamp = timestamp,
|
|
913
|
+
.amount = 10,
|
|
914
|
+
.debit_account_id = 7,
|
|
915
|
+
.credit_account_id = 8,
|
|
916
|
+
}),
|
|
917
|
+
},
|
|
918
|
+
Vector{
|
|
919
|
+
.result = .exists,
|
|
920
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
921
|
+
.id = 12,
|
|
922
|
+
.timestamp = timestamp + 1,
|
|
923
|
+
.amount = 10,
|
|
924
|
+
.debit_account_id = 7,
|
|
925
|
+
.credit_account_id = 8,
|
|
926
|
+
}),
|
|
927
|
+
},
|
|
928
|
+
Vector{
|
|
929
|
+
.result = .exists_with_different_debit_account_id,
|
|
930
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
931
|
+
.id = 12,
|
|
932
|
+
.timestamp = timestamp + 1,
|
|
933
|
+
.amount = 10,
|
|
934
|
+
.debit_account_id = 8,
|
|
935
|
+
.credit_account_id = 7,
|
|
936
|
+
}),
|
|
937
|
+
},
|
|
938
|
+
Vector{
|
|
939
|
+
.result = .exists_with_different_credit_account_id,
|
|
940
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
941
|
+
.id = 12,
|
|
942
|
+
.timestamp = timestamp + 1,
|
|
943
|
+
.amount = 10,
|
|
944
|
+
.debit_account_id = 7,
|
|
945
|
+
.credit_account_id = 1,
|
|
946
|
+
}),
|
|
947
|
+
},
|
|
948
|
+
Vector{
|
|
949
|
+
.result = .exists_with_different_amount,
|
|
950
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
951
|
+
.id = 12,
|
|
952
|
+
.timestamp = timestamp + 1,
|
|
953
|
+
.amount = 11,
|
|
954
|
+
.debit_account_id = 7,
|
|
955
|
+
.credit_account_id = 8,
|
|
956
|
+
}),
|
|
957
|
+
},
|
|
958
|
+
Vector{
|
|
959
|
+
.result = .exists_with_different_flags,
|
|
960
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
961
|
+
.id = 12,
|
|
962
|
+
.timestamp = timestamp + 1,
|
|
963
|
+
.amount = 10,
|
|
964
|
+
.debit_account_id = 7,
|
|
965
|
+
.credit_account_id = 8,
|
|
966
|
+
.flags = .{ .condition = true },
|
|
967
|
+
}),
|
|
968
|
+
},
|
|
969
|
+
Vector{
|
|
970
|
+
.result = .exists_with_different_user_data,
|
|
971
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
972
|
+
.id = 12,
|
|
973
|
+
.timestamp = timestamp + 1,
|
|
974
|
+
.amount = 10,
|
|
975
|
+
.debit_account_id = 7,
|
|
976
|
+
.credit_account_id = 8,
|
|
977
|
+
.user_data = 'A',
|
|
978
|
+
}),
|
|
979
|
+
},
|
|
980
|
+
Vector{
|
|
981
|
+
.result = .ok,
|
|
982
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
983
|
+
.id = 13,
|
|
984
|
+
.timestamp = timestamp + 1,
|
|
985
|
+
.amount = 10,
|
|
986
|
+
.debit_account_id = 7,
|
|
987
|
+
.credit_account_id = 8,
|
|
988
|
+
.flags = .{ .condition = true },
|
|
989
|
+
.reserved = [_]u8{1} ** 32,
|
|
990
|
+
}),
|
|
991
|
+
},
|
|
992
|
+
Vector{
|
|
993
|
+
.result = .exists_with_different_reserved_field,
|
|
994
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
995
|
+
.id = 13,
|
|
996
|
+
.timestamp = timestamp + 2,
|
|
997
|
+
.amount = 10,
|
|
998
|
+
.debit_account_id = 7,
|
|
999
|
+
.credit_account_id = 8,
|
|
1000
|
+
.flags = .{ .condition = true },
|
|
1001
|
+
.reserved = [_]u8{2} ** 32,
|
|
1002
|
+
}),
|
|
1003
|
+
},
|
|
1004
|
+
Vector{
|
|
1005
|
+
.result = .timeout_reserved_for_two_phase_commit,
|
|
1006
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
1007
|
+
.id = 13,
|
|
1008
|
+
.timestamp = timestamp + 2,
|
|
1009
|
+
.amount = 10,
|
|
1010
|
+
.debit_account_id = 7,
|
|
1011
|
+
.credit_account_id = 8,
|
|
1012
|
+
.flags = .{ .condition = true },
|
|
1013
|
+
.reserved = [_]u8{1} ** 32,
|
|
1014
|
+
.timeout = 10,
|
|
1015
|
+
}),
|
|
1016
|
+
},
|
|
1017
|
+
Vector{
|
|
1018
|
+
.result = .two_phase_commit_must_timeout,
|
|
1019
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
1020
|
+
.id = 14,
|
|
1021
|
+
.timestamp = timestamp + 2,
|
|
1022
|
+
.amount = 10,
|
|
1023
|
+
.debit_account_id = 7,
|
|
1024
|
+
.credit_account_id = 8,
|
|
1025
|
+
.flags = .{ .two_phase_commit = true },
|
|
1026
|
+
.timeout = 0,
|
|
1027
|
+
}),
|
|
1028
|
+
},
|
|
1029
|
+
Vector{
|
|
1030
|
+
.result = .ok,
|
|
1031
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
1032
|
+
.id = 15,
|
|
1033
|
+
.timestamp = timestamp + 2,
|
|
1034
|
+
.amount = 10,
|
|
1035
|
+
.debit_account_id = 7,
|
|
1036
|
+
.credit_account_id = 8,
|
|
1037
|
+
.flags = .{ .two_phase_commit = true },
|
|
1038
|
+
.timeout = 20,
|
|
1039
|
+
}),
|
|
1040
|
+
},
|
|
1041
|
+
Vector{
|
|
1042
|
+
.result = .exists_with_different_timeout,
|
|
1043
|
+
.object = std.mem.zeroInit(Transfer, .{
|
|
1044
|
+
.id = 15,
|
|
1045
|
+
.timestamp = timestamp + 3,
|
|
1046
|
+
.amount = 10,
|
|
1047
|
+
.debit_account_id = 7,
|
|
1048
|
+
.credit_account_id = 8,
|
|
1049
|
+
.flags = .{ .two_phase_commit = true },
|
|
1050
|
+
.timeout = 25,
|
|
1051
|
+
}),
|
|
1052
|
+
},
|
|
1053
|
+
};
|
|
1054
|
+
|
|
1055
|
+
for (vectors) |vector| {
|
|
1056
|
+
try testing.expectEqual(vector.result, state_machine.create_transfer(vector.object));
|
|
1057
|
+
if (vector.result == .ok) {
|
|
1058
|
+
try testing.expectEqual(vector.object, state_machine.get_transfer(vector.object.id).?.*);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// 2 phase commit [reserved]:
|
|
1063
|
+
try testing.expectEqual(@as(u64, 10), state_machine.get_account(7).?.*.debits_reserved);
|
|
1064
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(7).?.*.credits_reserved);
|
|
1065
|
+
try testing.expectEqual(@as(u64, 10), state_machine.get_account(8).?.*.credits_reserved);
|
|
1066
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(8).?.*.debits_reserved);
|
|
1067
|
+
// 1 phase commit [accepted]:
|
|
1068
|
+
try testing.expectEqual(@as(u64, 20), state_machine.get_account(7).?.*.debits_accepted);
|
|
1069
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(7).?.*.credits_accepted);
|
|
1070
|
+
try testing.expectEqual(@as(u64, 20), state_machine.get_account(8).?.*.credits_accepted);
|
|
1071
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(8).?.*.debits_accepted);
|
|
1072
|
+
|
|
1073
|
+
// Rollback transfer with id [12], amount of 10:
|
|
1074
|
+
state_machine.create_transfer_rollback(state_machine.get_transfer(vectors[11].object.id).?.*);
|
|
1075
|
+
try testing.expectEqual(@as(u64, 10), state_machine.get_account(7).?.*.debits_accepted);
|
|
1076
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(7).?.*.credits_accepted);
|
|
1077
|
+
try testing.expectEqual(@as(u64, 10), state_machine.get_account(8).?.*.credits_accepted);
|
|
1078
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(8).?.*.debits_accepted);
|
|
1079
|
+
try testing.expect(state_machine.get_transfer(vectors[11].object.id) == null);
|
|
1080
|
+
|
|
1081
|
+
// Rollback transfer with id [15], amount of 10:
|
|
1082
|
+
state_machine.create_transfer_rollback(state_machine.get_transfer(vectors[22].object.id).?.*);
|
|
1083
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(7).?.*.debits_reserved);
|
|
1084
|
+
try testing.expectEqual(@as(u64, 0), state_machine.get_account(8).?.*.credits_reserved);
|
|
1085
|
+
try testing.expect(state_machine.get_transfer(vectors[22].object.id) == null);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
test "create/lookup/rollback commits" {
|
|
1089
|
+
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
1090
|
+
defer arena.deinit();
|
|
1091
|
+
const allocator = &arena.allocator;
|
|
1092
|
+
|
|
1093
|
+
const Vector = struct { result: CommitTransferResult, object: Commit };
|
|
1094
|
+
|
|
1095
|
+
var accounts = [_]Account{
|
|
1096
|
+
std.mem.zeroInit(Account, .{ .id = 1 }),
|
|
1097
|
+
std.mem.zeroInit(Account, .{ .id = 2 }),
|
|
1098
|
+
std.mem.zeroInit(Account, .{ .id = 3 }),
|
|
1099
|
+
std.mem.zeroInit(Account, .{ .id = 4 }),
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
var transfers = [_]Transfer{
|
|
1103
|
+
std.mem.zeroInit(Transfer, .{
|
|
1104
|
+
.id = 1,
|
|
1105
|
+
.amount = 15,
|
|
1106
|
+
.debit_account_id = 1,
|
|
1107
|
+
.credit_account_id = 2,
|
|
1108
|
+
}),
|
|
1109
|
+
std.mem.zeroInit(Transfer, .{
|
|
1110
|
+
.id = 2,
|
|
1111
|
+
.amount = 15,
|
|
1112
|
+
.debit_account_id = 1,
|
|
1113
|
+
.credit_account_id = 2,
|
|
1114
|
+
.flags = .{ .two_phase_commit = true },
|
|
1115
|
+
.timeout = 25,
|
|
1116
|
+
}),
|
|
1117
|
+
std.mem.zeroInit(Transfer, .{
|
|
1118
|
+
.id = 3,
|
|
1119
|
+
.amount = 15,
|
|
1120
|
+
.debit_account_id = 1,
|
|
1121
|
+
.credit_account_id = 2,
|
|
1122
|
+
.flags = .{ .two_phase_commit = true },
|
|
1123
|
+
.timeout = 25,
|
|
1124
|
+
}),
|
|
1125
|
+
std.mem.zeroInit(Transfer, .{
|
|
1126
|
+
.id = 4,
|
|
1127
|
+
.amount = 15,
|
|
1128
|
+
.debit_account_id = 1,
|
|
1129
|
+
.credit_account_id = 2,
|
|
1130
|
+
.flags = .{ .two_phase_commit = true },
|
|
1131
|
+
.timeout = 1,
|
|
1132
|
+
}),
|
|
1133
|
+
std.mem.zeroInit(Transfer, .{
|
|
1134
|
+
.id = 5,
|
|
1135
|
+
.amount = 15,
|
|
1136
|
+
.debit_account_id = 1,
|
|
1137
|
+
.credit_account_id = 2,
|
|
1138
|
+
.flags = .{
|
|
1139
|
+
.two_phase_commit = true,
|
|
1140
|
+
.condition = true,
|
|
1141
|
+
},
|
|
1142
|
+
.timeout = 25,
|
|
1143
|
+
}),
|
|
1144
|
+
std.mem.zeroInit(Transfer, .{
|
|
1145
|
+
.id = 6,
|
|
1146
|
+
.amount = 15,
|
|
1147
|
+
.debit_account_id = 1,
|
|
1148
|
+
.credit_account_id = 2,
|
|
1149
|
+
.flags = .{
|
|
1150
|
+
.two_phase_commit = true,
|
|
1151
|
+
.condition = false,
|
|
1152
|
+
},
|
|
1153
|
+
.timeout = 25,
|
|
1154
|
+
}),
|
|
1155
|
+
std.mem.zeroInit(Transfer, .{
|
|
1156
|
+
.id = 7,
|
|
1157
|
+
.amount = 15,
|
|
1158
|
+
.debit_account_id = 3,
|
|
1159
|
+
.credit_account_id = 4,
|
|
1160
|
+
.flags = .{ .two_phase_commit = true },
|
|
1161
|
+
.timeout = 25,
|
|
1162
|
+
}),
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
var state_machine = try StateMachine.init(allocator, accounts.len, transfers.len, 1);
|
|
1166
|
+
defer state_machine.deinit();
|
|
1167
|
+
|
|
1168
|
+
const input = std.mem.asBytes(&accounts);
|
|
1169
|
+
const output = try allocator.alloc(u8, 4096);
|
|
1170
|
+
|
|
1171
|
+
// Accounts:
|
|
1172
|
+
state_machine.prepare(0, .create_accounts, input);
|
|
1173
|
+
const size = state_machine.commit(0, .create_accounts, input, output);
|
|
1174
|
+
const results = std.mem.bytesAsSlice(CreateAccountsResult, output[0..size]);
|
|
1175
|
+
|
|
1176
|
+
for (accounts) |account, i| {
|
|
1177
|
+
try testing.expectEqual(accounts[i], state_machine.get_account(accounts[i].id).?.*);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Transfers:
|
|
1181
|
+
const object_transfers = std.mem.asBytes(&transfers);
|
|
1182
|
+
const output_transfers = try allocator.alloc(u8, 4096);
|
|
1183
|
+
|
|
1184
|
+
state_machine.prepare(0, .create_transfers, object_transfers);
|
|
1185
|
+
const size_transfers = state_machine.commit(0, .create_transfers, object_transfers, output_transfers);
|
|
1186
|
+
const results_transfers = std.mem.bytesAsSlice(CreateTransfersResult, output_transfers[0..size_transfers]);
|
|
1187
|
+
|
|
1188
|
+
for (transfers) |transfer, i| {
|
|
1189
|
+
try testing.expectEqual(transfers[i], state_machine.get_transfer(transfers[i].id).?.*);
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// Commits:
|
|
1193
|
+
const timestamp: u64 = (state_machine.commit_timestamp + 1);
|
|
1194
|
+
const vectors = [_]Vector{
|
|
1195
|
+
Vector{
|
|
1196
|
+
.result = .reserved_field,
|
|
1197
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1198
|
+
.id = 1,
|
|
1199
|
+
.timestamp = timestamp,
|
|
1200
|
+
.reserved = [_]u8{1} ** 32,
|
|
1201
|
+
}),
|
|
1202
|
+
},
|
|
1203
|
+
Vector{
|
|
1204
|
+
.result = .reserved_flag_padding,
|
|
1205
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1206
|
+
.id = 1,
|
|
1207
|
+
.timestamp = timestamp,
|
|
1208
|
+
.flags = .{ .padding = 1 },
|
|
1209
|
+
}),
|
|
1210
|
+
},
|
|
1211
|
+
Vector{
|
|
1212
|
+
.result = .transfer_not_found,
|
|
1213
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1214
|
+
.id = 777,
|
|
1215
|
+
.timestamp = timestamp,
|
|
1216
|
+
}),
|
|
1217
|
+
},
|
|
1218
|
+
Vector{
|
|
1219
|
+
.result = .transfer_not_two_phase_commit,
|
|
1220
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1221
|
+
.id = 1,
|
|
1222
|
+
.timestamp = timestamp,
|
|
1223
|
+
}),
|
|
1224
|
+
},
|
|
1225
|
+
Vector{
|
|
1226
|
+
.result = .ok,
|
|
1227
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1228
|
+
.id = 2,
|
|
1229
|
+
.timestamp = timestamp,
|
|
1230
|
+
}),
|
|
1231
|
+
},
|
|
1232
|
+
Vector{
|
|
1233
|
+
.result = .already_committed_but_accepted,
|
|
1234
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1235
|
+
.id = 2,
|
|
1236
|
+
.timestamp = timestamp + 1,
|
|
1237
|
+
.flags = .{ .reject = true },
|
|
1238
|
+
}),
|
|
1239
|
+
},
|
|
1240
|
+
Vector{
|
|
1241
|
+
.result = .already_committed,
|
|
1242
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1243
|
+
.id = 2,
|
|
1244
|
+
.timestamp = timestamp + 1,
|
|
1245
|
+
}),
|
|
1246
|
+
},
|
|
1247
|
+
Vector{
|
|
1248
|
+
.result = .ok,
|
|
1249
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1250
|
+
.id = 3,
|
|
1251
|
+
.timestamp = timestamp + 1,
|
|
1252
|
+
.flags = .{ .reject = true },
|
|
1253
|
+
}),
|
|
1254
|
+
},
|
|
1255
|
+
Vector{
|
|
1256
|
+
.result = .already_committed_but_rejected,
|
|
1257
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1258
|
+
.id = 3,
|
|
1259
|
+
.timestamp = timestamp + 2,
|
|
1260
|
+
}),
|
|
1261
|
+
},
|
|
1262
|
+
Vector{
|
|
1263
|
+
.result = .transfer_expired,
|
|
1264
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1265
|
+
.id = 4,
|
|
1266
|
+
.timestamp = timestamp + 2,
|
|
1267
|
+
}),
|
|
1268
|
+
},
|
|
1269
|
+
Vector{
|
|
1270
|
+
.result = .condition_requires_preimage,
|
|
1271
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1272
|
+
.id = 5,
|
|
1273
|
+
.timestamp = timestamp + 2,
|
|
1274
|
+
}),
|
|
1275
|
+
},
|
|
1276
|
+
Vector{
|
|
1277
|
+
.result = .preimage_invalid,
|
|
1278
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1279
|
+
.id = 5,
|
|
1280
|
+
.timestamp = timestamp + 2,
|
|
1281
|
+
.flags = .{ .preimage = true },
|
|
1282
|
+
.reserved = [_]u8{1} ** 32,
|
|
1283
|
+
}),
|
|
1284
|
+
},
|
|
1285
|
+
Vector{
|
|
1286
|
+
.result = .preimage_requires_condition,
|
|
1287
|
+
.object = std.mem.zeroInit(Commit, .{
|
|
1288
|
+
.id = 6,
|
|
1289
|
+
.timestamp = timestamp + 2,
|
|
1290
|
+
.flags = .{ .preimage = true },
|
|
1291
|
+
}),
|
|
1292
|
+
},
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
// Test balances BEFORE commit
|
|
1296
|
+
// Account 1:
|
|
1297
|
+
const account_1_before = state_machine.get_account(1).?.*;
|
|
1298
|
+
try testing.expectEqual(@as(u64, 15), account_1_before.debits_accepted);
|
|
1299
|
+
try testing.expectEqual(@as(u64, 75), account_1_before.debits_reserved);
|
|
1300
|
+
try testing.expectEqual(@as(u64, 0), account_1_before.credits_accepted);
|
|
1301
|
+
try testing.expectEqual(@as(u64, 0), account_1_before.credits_reserved);
|
|
1302
|
+
// Account 2:
|
|
1303
|
+
const account_2_before = state_machine.get_account(2).?.*;
|
|
1304
|
+
try testing.expectEqual(@as(u64, 0), account_2_before.debits_accepted);
|
|
1305
|
+
try testing.expectEqual(@as(u64, 0), account_2_before.debits_reserved);
|
|
1306
|
+
try testing.expectEqual(@as(u64, 15), account_2_before.credits_accepted);
|
|
1307
|
+
try testing.expectEqual(@as(u64, 75), account_2_before.credits_reserved);
|
|
1308
|
+
|
|
1309
|
+
for (vectors) |vector| {
|
|
1310
|
+
try testing.expectEqual(vector.result, state_machine.commit_transfer(vector.object));
|
|
1311
|
+
if (vector.result == .ok) {
|
|
1312
|
+
try testing.expectEqual(vector.object, state_machine.get_commit(vector.object.id).?.*);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Test balances AFTER commit
|
|
1317
|
+
// Account 1:
|
|
1318
|
+
const account_1_after = state_machine.get_account(1).?.*;
|
|
1319
|
+
try testing.expectEqual(@as(u64, 30), account_1_after.debits_accepted);
|
|
1320
|
+
// +15 (acceptance applied):
|
|
1321
|
+
try testing.expectEqual(@as(u64, 45), account_1_after.debits_reserved);
|
|
1322
|
+
// -15 (reserved moved):
|
|
1323
|
+
try testing.expectEqual(@as(u64, 0), account_1_after.credits_accepted);
|
|
1324
|
+
try testing.expectEqual(@as(u64, 0), account_1_after.credits_reserved);
|
|
1325
|
+
// Account 2:
|
|
1326
|
+
const account_2_after = state_machine.get_account(2).?.*;
|
|
1327
|
+
try testing.expectEqual(@as(u64, 0), account_2_after.debits_accepted);
|
|
1328
|
+
try testing.expectEqual(@as(u64, 0), account_2_after.debits_reserved);
|
|
1329
|
+
// +15 (acceptance applied):
|
|
1330
|
+
try testing.expectEqual(@as(u64, 30), account_2_after.credits_accepted);
|
|
1331
|
+
// -15 (reserved moved):
|
|
1332
|
+
try testing.expectEqual(@as(u64, 45), account_2_after.credits_reserved);
|
|
1333
|
+
|
|
1334
|
+
// Test COMMIT with invalid debit/credit accounts
|
|
1335
|
+
state_machine.create_account_rollback(accounts[3]);
|
|
1336
|
+
try testing.expect(state_machine.get_account(accounts[3].id) == null);
|
|
1337
|
+
try testing.expectEqual(
|
|
1338
|
+
state_machine.commit_transfer(std.mem.zeroInit(Commit, .{
|
|
1339
|
+
.id = 7,
|
|
1340
|
+
.timestamp = timestamp + 2,
|
|
1341
|
+
})),
|
|
1342
|
+
.credit_account_not_found,
|
|
1343
|
+
);
|
|
1344
|
+
state_machine.create_account_rollback(accounts[2]);
|
|
1345
|
+
try testing.expect(state_machine.get_account(accounts[2].id) == null);
|
|
1346
|
+
try testing.expectEqual(
|
|
1347
|
+
state_machine.commit_transfer(std.mem.zeroInit(Commit, .{
|
|
1348
|
+
.id = 7,
|
|
1349
|
+
.timestamp = timestamp + 2,
|
|
1350
|
+
})),
|
|
1351
|
+
.debit_account_not_found,
|
|
1352
|
+
);
|
|
1353
|
+
|
|
1354
|
+
// Rollback [id=2] not rejected:
|
|
1355
|
+
state_machine.commit_transfer_rollback(vectors[4].object);
|
|
1356
|
+
|
|
1357
|
+
// Account 1:
|
|
1358
|
+
const account_1_rollback = state_machine.get_account(1).?.*;
|
|
1359
|
+
// -15 (rollback):
|
|
1360
|
+
try testing.expectEqual(@as(u64, 15), account_1_rollback.debits_accepted);
|
|
1361
|
+
try testing.expectEqual(@as(u64, 60), account_1_rollback.debits_reserved);
|
|
1362
|
+
try testing.expectEqual(@as(u64, 0), account_1_rollback.credits_accepted);
|
|
1363
|
+
try testing.expectEqual(@as(u64, 0), account_1_rollback.credits_reserved);
|
|
1364
|
+
// Account 2:
|
|
1365
|
+
const account_2_rollback = state_machine.get_account(2).?.*;
|
|
1366
|
+
try testing.expectEqual(@as(u64, 0), account_2_rollback.debits_accepted);
|
|
1367
|
+
try testing.expectEqual(@as(u64, 0), account_2_rollback.debits_reserved);
|
|
1368
|
+
// -15 (rollback):
|
|
1369
|
+
try testing.expectEqual(@as(u64, 15), account_2_rollback.credits_accepted);
|
|
1370
|
+
try testing.expectEqual(@as(u64, 60), account_2_rollback.credits_reserved);
|
|
1371
|
+
|
|
1372
|
+
// Rollback [id=3] rejected:
|
|
1373
|
+
state_machine.commit_transfer_rollback(vectors[7].object);
|
|
1374
|
+
// Account 1:
|
|
1375
|
+
const account_1_rollback_reject = state_machine.get_account(1).?.*;
|
|
1376
|
+
try testing.expectEqual(@as(u64, 15), account_1_rollback_reject.debits_accepted);
|
|
1377
|
+
// Remains unchanged:
|
|
1378
|
+
try testing.expectEqual(@as(u64, 75), account_1_rollback_reject.debits_reserved);
|
|
1379
|
+
// +15 rolled back:
|
|
1380
|
+
try testing.expectEqual(@as(u64, 0), account_1_rollback_reject.credits_accepted);
|
|
1381
|
+
try testing.expectEqual(@as(u64, 0), account_1_rollback_reject.credits_reserved);
|
|
1382
|
+
// Account 2:
|
|
1383
|
+
const account_2_rollback_reject = state_machine.get_account(2).?.*;
|
|
1384
|
+
try testing.expectEqual(@as(u64, 0), account_2_rollback_reject.debits_accepted);
|
|
1385
|
+
try testing.expectEqual(@as(u64, 0), account_2_rollback_reject.debits_reserved);
|
|
1386
|
+
try testing.expectEqual(@as(u64, 15), account_2_rollback_reject.credits_accepted);
|
|
1387
|
+
// +15 rolled back"
|
|
1388
|
+
try testing.expectEqual(@as(u64, 75), account_2_rollback_reject.credits_reserved);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
583
1391
|
fn test_routine_zeroed(comptime len: usize) !void {
|
|
584
1392
|
const routine = switch (len) {
|
|
585
1393
|
32 => zeroed_32_bytes,
|