tigerbeetle-node 0.10.0 → 0.11.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 +302 -101
- package/dist/index.d.ts +70 -72
- package/dist/index.js +70 -72
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
- package/scripts/download_node_headers.sh +14 -7
- package/src/index.ts +6 -10
- package/src/node.zig +6 -3
- package/src/tigerbeetle/scripts/benchmark.sh +4 -4
- package/src/tigerbeetle/scripts/confirm_image.sh +44 -0
- package/src/tigerbeetle/scripts/fuzz_loop.sh +15 -0
- package/src/tigerbeetle/scripts/fuzz_unique_errors.sh +7 -0
- package/src/tigerbeetle/scripts/install.sh +19 -4
- package/src/tigerbeetle/scripts/install_zig.bat +5 -1
- package/src/tigerbeetle/scripts/install_zig.sh +24 -14
- package/src/tigerbeetle/scripts/pre-commit.sh +9 -0
- package/src/tigerbeetle/scripts/shellcheck.sh +5 -0
- package/src/tigerbeetle/scripts/tests_on_alpine.sh +10 -0
- package/src/tigerbeetle/scripts/tests_on_ubuntu.sh +14 -0
- package/src/tigerbeetle/scripts/validate_docs.sh +17 -0
- package/src/tigerbeetle/src/benchmark.zig +29 -13
- package/src/tigerbeetle/src/c/tb_client/context.zig +248 -47
- package/src/tigerbeetle/src/c/tb_client/echo_client.zig +108 -0
- package/src/tigerbeetle/src/c/tb_client/packet.zig +2 -2
- package/src/tigerbeetle/src/c/tb_client/signal.zig +2 -4
- package/src/tigerbeetle/src/c/tb_client/thread.zig +17 -257
- package/src/tigerbeetle/src/c/tb_client.h +118 -84
- package/src/tigerbeetle/src/c/tb_client.zig +88 -23
- package/src/tigerbeetle/src/c/tb_client_header_test.zig +135 -0
- package/src/tigerbeetle/src/c/test.zig +371 -1
- package/src/tigerbeetle/src/cli.zig +37 -7
- package/src/tigerbeetle/src/config.zig +58 -17
- package/src/tigerbeetle/src/demo.zig +5 -2
- package/src/tigerbeetle/src/demo_01_create_accounts.zig +1 -1
- package/src/tigerbeetle/src/demo_03_create_transfers.zig +13 -0
- package/src/tigerbeetle/src/ewah.zig +11 -33
- package/src/tigerbeetle/src/ewah_benchmark.zig +8 -9
- package/src/tigerbeetle/src/io/linux.zig +1 -1
- package/src/tigerbeetle/src/lsm/README.md +308 -0
- package/src/tigerbeetle/src/lsm/binary_search.zig +137 -10
- package/src/tigerbeetle/src/lsm/bloom_filter.zig +43 -0
- package/src/tigerbeetle/src/lsm/compaction.zig +376 -397
- package/src/tigerbeetle/src/lsm/composite_key.zig +2 -0
- package/src/tigerbeetle/src/lsm/eytzinger.zig +1 -1
- package/src/tigerbeetle/src/{eytzinger_benchmark.zig → lsm/eytzinger_benchmark.zig} +34 -21
- package/src/tigerbeetle/src/lsm/forest.zig +21 -447
- package/src/tigerbeetle/src/lsm/forest_fuzz.zig +414 -0
- package/src/tigerbeetle/src/lsm/grid.zig +170 -76
- package/src/tigerbeetle/src/lsm/groove.zig +197 -133
- package/src/tigerbeetle/src/lsm/k_way_merge.zig +40 -18
- package/src/tigerbeetle/src/lsm/level_iterator.zig +28 -9
- package/src/tigerbeetle/src/lsm/manifest.zig +93 -180
- package/src/tigerbeetle/src/lsm/manifest_level.zig +161 -454
- package/src/tigerbeetle/src/lsm/manifest_log.zig +243 -356
- package/src/tigerbeetle/src/lsm/manifest_log_fuzz.zig +665 -0
- package/src/tigerbeetle/src/lsm/node_pool.zig +4 -0
- package/src/tigerbeetle/src/lsm/posted_groove.zig +65 -76
- package/src/tigerbeetle/src/lsm/segmented_array.zig +580 -251
- package/src/tigerbeetle/src/lsm/segmented_array_benchmark.zig +148 -0
- package/src/tigerbeetle/src/lsm/segmented_array_fuzz.zig +9 -0
- package/src/tigerbeetle/src/lsm/set_associative_cache.zig +62 -12
- package/src/tigerbeetle/src/lsm/table.zig +115 -68
- package/src/tigerbeetle/src/lsm/table_immutable.zig +30 -23
- package/src/tigerbeetle/src/lsm/table_iterator.zig +27 -17
- package/src/tigerbeetle/src/lsm/table_mutable.zig +63 -12
- package/src/tigerbeetle/src/lsm/test.zig +61 -56
- package/src/tigerbeetle/src/lsm/tree.zig +450 -407
- package/src/tigerbeetle/src/lsm/tree_fuzz.zig +461 -0
- package/src/tigerbeetle/src/main.zig +83 -8
- package/src/tigerbeetle/src/message_bus.zig +20 -9
- package/src/tigerbeetle/src/message_pool.zig +22 -19
- package/src/tigerbeetle/src/ring_buffer.zig +7 -3
- package/src/tigerbeetle/src/simulator.zig +179 -119
- package/src/tigerbeetle/src/state_machine.zig +381 -246
- package/src/tigerbeetle/src/static_allocator.zig +65 -0
- package/src/tigerbeetle/src/storage.zig +3 -7
- package/src/tigerbeetle/src/test/accounting/auditor.zig +577 -0
- package/src/tigerbeetle/src/test/accounting/workload.zig +823 -0
- package/src/tigerbeetle/src/test/cluster.zig +33 -81
- package/src/tigerbeetle/src/test/conductor.zig +366 -0
- package/src/tigerbeetle/src/test/fuzz.zig +121 -0
- package/src/tigerbeetle/src/test/id.zig +89 -0
- package/src/tigerbeetle/src/test/network.zig +45 -19
- package/src/tigerbeetle/src/test/packet_simulator.zig +40 -29
- package/src/tigerbeetle/src/test/priority_queue.zig +645 -0
- package/src/tigerbeetle/src/test/state_checker.zig +91 -69
- package/src/tigerbeetle/src/test/state_machine.zig +11 -35
- package/src/tigerbeetle/src/test/storage.zig +470 -106
- package/src/tigerbeetle/src/test/storage_checker.zig +204 -0
- package/src/tigerbeetle/src/tigerbeetle.zig +15 -16
- package/src/tigerbeetle/src/unit_tests.zig +13 -1
- package/src/tigerbeetle/src/util.zig +97 -11
- package/src/tigerbeetle/src/vopr.zig +495 -0
- package/src/tigerbeetle/src/vsr/client.zig +21 -3
- package/src/tigerbeetle/src/vsr/journal.zig +293 -212
- package/src/tigerbeetle/src/vsr/replica.zig +1086 -515
- package/src/tigerbeetle/src/vsr/superblock.zig +382 -637
- package/src/tigerbeetle/src/vsr/superblock_client_table.zig +14 -16
- package/src/tigerbeetle/src/vsr/superblock_free_set.zig +416 -153
- package/src/tigerbeetle/src/vsr/superblock_free_set_fuzz.zig +332 -0
- package/src/tigerbeetle/src/vsr/superblock_fuzz.zig +349 -0
- package/src/tigerbeetle/src/vsr/superblock_manifest.zig +62 -12
- package/src/tigerbeetle/src/vsr/superblock_quorums.zig +394 -0
- package/src/tigerbeetle/src/vsr/superblock_quorums_fuzz.zig +312 -0
- package/src/tigerbeetle/src/vsr.zig +94 -60
- package/src/tigerbeetle/scripts/vopr.bat +0 -48
- package/src/tigerbeetle/scripts/vopr.sh +0 -33
- package/src/tigerbeetle/src/benchmark_array_search.zig +0 -317
- package/src/tigerbeetle/src/benchmarks/perf.zig +0 -299
|
@@ -3,11 +3,13 @@ const assert = std.debug.assert;
|
|
|
3
3
|
const mem = std.mem;
|
|
4
4
|
|
|
5
5
|
const config = @import("../config.zig");
|
|
6
|
+
const vsr = @import("../vsr.zig");
|
|
6
7
|
|
|
7
8
|
const Cluster = @import("cluster.zig").Cluster;
|
|
8
9
|
const Network = @import("network.zig").Network;
|
|
9
10
|
const Storage = @import("storage.zig").Storage;
|
|
10
|
-
const
|
|
11
|
+
const Client = @import("cluster.zig").Client;
|
|
12
|
+
const Replica = @import("cluster.zig").Replica;
|
|
11
13
|
|
|
12
14
|
const message_pool = @import("../message_pool.zig");
|
|
13
15
|
const MessagePool = message_pool.MessagePool;
|
|
@@ -15,47 +17,50 @@ const Message = MessagePool.Message;
|
|
|
15
17
|
|
|
16
18
|
const RingBuffer = @import("../ring_buffer.zig").RingBuffer;
|
|
17
19
|
|
|
18
|
-
const RequestQueue = RingBuffer(u128, config.client_request_queue_max, .array);
|
|
19
20
|
const StateTransitions = std.AutoHashMap(u128, u64);
|
|
20
21
|
|
|
21
22
|
const log = std.log.scoped(.state_checker);
|
|
22
23
|
|
|
23
24
|
pub const StateChecker = struct {
|
|
24
|
-
/// Indexed by client index as used by Cluster.
|
|
25
|
-
client_requests: [config.clients_max]RequestQueue =
|
|
26
|
-
[_]RequestQueue{.{}} ** config.clients_max,
|
|
27
|
-
|
|
28
25
|
/// Indexed by replica index.
|
|
29
|
-
|
|
26
|
+
replica_states: [config.replicas_max]u128 = [_]u128{0} ** config.replicas_max,
|
|
30
27
|
|
|
28
|
+
/// Keyed by committed `message.header.checksum`.
|
|
29
|
+
///
|
|
30
|
+
/// The first state is always `root_prepare.checksum`, since the root prepare doesn't
|
|
31
|
+
/// commit normally.
|
|
31
32
|
history: StateTransitions,
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
state: u128,
|
|
34
|
+
root: u128,
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// TODO When StateChecker is owned by the Simulation, use @fieldParentPtr to get these.
|
|
37
|
+
replicas: []const Replica,
|
|
38
|
+
clients: []const Client,
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
/// The highest canonical state reached by the cluster.
|
|
41
|
+
state: u128 = 0,
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const state_machine = &replica.state_machine;
|
|
45
|
-
assert(state_machine.state == state);
|
|
46
|
-
state_machine_states[i] = state_machine.state;
|
|
47
|
-
}
|
|
43
|
+
/// The number of times the canonical state has been advanced.
|
|
44
|
+
requests_committed: u64 = 0,
|
|
48
45
|
|
|
46
|
+
pub fn init(
|
|
47
|
+
allocator: mem.Allocator,
|
|
48
|
+
cluster: u32,
|
|
49
|
+
replicas: []const Replica,
|
|
50
|
+
clients: []const Client,
|
|
51
|
+
) !StateChecker {
|
|
49
52
|
var history = StateTransitions.init(allocator);
|
|
50
53
|
errdefer history.deinit();
|
|
51
54
|
|
|
55
|
+
const root_checksum = vsr.Header.root_prepare(cluster).checksum;
|
|
56
|
+
|
|
52
57
|
var state_checker = StateChecker{
|
|
53
|
-
.state_machine_states = state_machine_states,
|
|
54
58
|
.history = history,
|
|
55
|
-
.
|
|
59
|
+
.root = root_checksum,
|
|
60
|
+
.replicas = replicas,
|
|
61
|
+
.clients = clients,
|
|
56
62
|
};
|
|
57
|
-
|
|
58
|
-
try state_checker.history.putNoClobber(state, state_checker.transitions);
|
|
63
|
+
try state_checker.history.putNoClobber(root_checksum, state_checker.requests_committed);
|
|
59
64
|
|
|
60
65
|
return state_checker;
|
|
61
66
|
}
|
|
@@ -64,14 +69,24 @@ pub const StateChecker = struct {
|
|
|
64
69
|
state_checker.history.deinit();
|
|
65
70
|
}
|
|
66
71
|
|
|
67
|
-
pub fn check_state(state_checker: *StateChecker,
|
|
68
|
-
const
|
|
72
|
+
pub fn check_state(state_checker: *StateChecker, replica_index: u8) !void {
|
|
73
|
+
const replica = state_checker.replicas[replica_index];
|
|
74
|
+
const commit_header = header: {
|
|
75
|
+
if (replica.journal.recovered) {
|
|
76
|
+
const commit_header = replica.journal.header_with_op(replica.commit_min);
|
|
77
|
+
assert(commit_header != null or replica.commit_min == replica.op_checkpoint);
|
|
78
|
+
break :header replica.journal.header_with_op(replica.commit_min);
|
|
79
|
+
} else {
|
|
80
|
+
// Still recovering.
|
|
81
|
+
break :header null;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
69
84
|
|
|
70
|
-
const a = state_checker.
|
|
71
|
-
const b =
|
|
85
|
+
const a = state_checker.replica_states[replica_index];
|
|
86
|
+
const b = if (commit_header) |h| h.checksum else state_checker.root;
|
|
72
87
|
|
|
73
88
|
if (b == a) return;
|
|
74
|
-
state_checker.
|
|
89
|
+
state_checker.replica_states[replica_index] = b;
|
|
75
90
|
|
|
76
91
|
// If some other replica has already reached this state, then it will be in the history:
|
|
77
92
|
if (state_checker.history.get(b)) |transition| {
|
|
@@ -81,65 +96,72 @@ pub const StateChecker = struct {
|
|
|
81
96
|
// transitioned may not regress.
|
|
82
97
|
log.info(
|
|
83
98
|
"{d:0>4}/{d:0>4} {x:0>32} > {x:0>32} {}",
|
|
84
|
-
.{ transition, state_checker.
|
|
99
|
+
.{ transition, state_checker.requests_committed, a, b, replica_index },
|
|
85
100
|
);
|
|
86
101
|
return;
|
|
87
102
|
}
|
|
88
103
|
|
|
104
|
+
if (commit_header == null) return;
|
|
105
|
+
assert(commit_header.?.parent == a);
|
|
106
|
+
assert(commit_header.?.op > 0);
|
|
107
|
+
assert(commit_header.?.command == .prepare);
|
|
108
|
+
assert(commit_header.?.operation != .reserved);
|
|
109
|
+
|
|
89
110
|
// The replica has transitioned to state `b` that is not yet in the history.
|
|
90
|
-
// Check if this is a valid new state based on
|
|
91
|
-
for (state_checker.
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
} else {
|
|
98
|
-
assert(transitions_executed == state_checker.transitions);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
state_checker.state = b;
|
|
102
|
-
state_checker.transitions += 1;
|
|
103
|
-
|
|
104
|
-
log.info(" {d:0>4} {x:0>32} > {x:0>32} {}", .{
|
|
105
|
-
state_checker.transitions,
|
|
106
|
-
a,
|
|
107
|
-
b,
|
|
108
|
-
replica,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
state_checker.history.putNoClobber(b, state_checker.transitions) catch {
|
|
112
|
-
@panic("state checker unable to allocate memory for history.put()");
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// As soon as we reach a valid state we must pop the inflight request.
|
|
116
|
-
// We cannot wait until the client receives the reply because that would allow
|
|
117
|
-
// the inflight request to be used to reach other states in the interim.
|
|
118
|
-
// We must therefore use our own queue rather than the clients' queues.
|
|
119
|
-
_ = queue.pop();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
111
|
+
// Check if this is a valid new state based on the originating client's inflight request.
|
|
112
|
+
const client = for (state_checker.clients) |*client| {
|
|
113
|
+
if (client.id == commit_header.?.client) break client;
|
|
114
|
+
} else unreachable;
|
|
115
|
+
|
|
116
|
+
if (client.request_queue.empty()) {
|
|
117
|
+
return error.ReplicaTransitionedToInvalidState;
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
|
|
120
|
+
const request = client.request_queue.head_ptr_const().?;
|
|
121
|
+
assert(request.message.header.client == commit_header.?.client);
|
|
122
|
+
assert(request.message.header.request == commit_header.?.request);
|
|
123
|
+
assert(request.message.header.command == .request);
|
|
124
|
+
assert(request.message.header.operation == commit_header.?.operation);
|
|
125
|
+
assert(request.message.header.size == commit_header.?.size);
|
|
126
|
+
// `checksum_body` will not match; the leader's StateMachine updated the timestamps in the
|
|
127
|
+
// prepare body's accounts/transfers.
|
|
128
|
+
|
|
129
|
+
const transitions_executed = state_checker.history.get(a).?;
|
|
130
|
+
if (transitions_executed < state_checker.requests_committed) {
|
|
131
|
+
return error.ReplicaSkippedInterimTransitions;
|
|
132
|
+
} else {
|
|
133
|
+
assert(transitions_executed == state_checker.requests_committed);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
state_checker.state = b;
|
|
137
|
+
state_checker.requests_committed += 1;
|
|
138
|
+
assert(state_checker.requests_committed == commit_header.?.op);
|
|
139
|
+
|
|
140
|
+
log.info(" {d:0>4} {x:0>32} > {x:0>32} {}", .{
|
|
141
|
+
state_checker.requests_committed,
|
|
142
|
+
a,
|
|
143
|
+
b,
|
|
144
|
+
replica_index,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
state_checker.history.putNoClobber(b, state_checker.requests_committed) catch {
|
|
148
|
+
@panic("state checker unable to allocate memory for history.put()");
|
|
149
|
+
};
|
|
126
150
|
}
|
|
127
151
|
|
|
128
152
|
pub fn convergence(state_checker: *StateChecker) bool {
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
const a = state_checker.state_machine_states[0];
|
|
132
|
-
for (state_checker.state_machine_states[1..cluster.options.replica_count]) |b| {
|
|
153
|
+
const a = state_checker.replica_states[0];
|
|
154
|
+
for (state_checker.replica_states[1..state_checker.replicas[0].replica_count]) |b| {
|
|
133
155
|
if (b != a) return false;
|
|
134
156
|
}
|
|
135
157
|
|
|
136
158
|
const transitions_executed = state_checker.history.get(a).?;
|
|
137
|
-
if (transitions_executed < state_checker.
|
|
159
|
+
if (transitions_executed < state_checker.requests_committed) {
|
|
138
160
|
// Cluster reached convergence but on a regressed state.
|
|
139
161
|
// A replica reached the transition limit, crashed, then repaired.
|
|
140
162
|
return false;
|
|
141
163
|
} else {
|
|
142
|
-
assert(transitions_executed == state_checker.
|
|
164
|
+
assert(transitions_executed == state_checker.requests_committed);
|
|
143
165
|
}
|
|
144
166
|
|
|
145
167
|
return true;
|
|
@@ -15,7 +15,7 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
15
15
|
root,
|
|
16
16
|
register,
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
random,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/// Minimum/mean number of ticks to perform the specified operation.
|
|
@@ -28,7 +28,6 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
options: Options,
|
|
31
|
-
state: u128,
|
|
32
31
|
prng: std.rand.DefaultPrng,
|
|
33
32
|
prepare_timestamp: u64 = 0,
|
|
34
33
|
commit_timestamp: u64 = 0,
|
|
@@ -38,7 +37,6 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
38
37
|
|
|
39
38
|
pub fn init(_: std.mem.Allocator, _: *Grid, options: Options) !StateMachine {
|
|
40
39
|
return StateMachine{
|
|
41
|
-
.state = hash(0, std.mem.asBytes(&options.seed)),
|
|
42
40
|
.options = options,
|
|
43
41
|
.prng = std.rand.DefaultPrng.init(options.seed),
|
|
44
42
|
};
|
|
@@ -46,6 +44,10 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
46
44
|
|
|
47
45
|
pub fn deinit(_: *StateMachine, _: std.mem.Allocator) void {}
|
|
48
46
|
|
|
47
|
+
// TODO This is dead code — tick() has been removed from the StateMachine
|
|
48
|
+
// interface. If we start using the test StateMachine again, tick will need
|
|
49
|
+
// to be called explicitly from the simulator to ensure async operations can
|
|
50
|
+
// finish.
|
|
49
51
|
pub fn tick(state_machine: *StateMachine) void {
|
|
50
52
|
if (state_machine.callback) |callback| {
|
|
51
53
|
if (state_machine.callback_ticks == 0) {
|
|
@@ -98,32 +100,16 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
98
100
|
input: []const u8,
|
|
99
101
|
output: []u8,
|
|
100
102
|
) usize {
|
|
101
|
-
_ =
|
|
103
|
+
_ = state_machine;
|
|
104
|
+
_ = client;
|
|
105
|
+
_ = input;
|
|
106
|
+
_ = output;
|
|
107
|
+
assert(op != 0);
|
|
102
108
|
|
|
103
109
|
switch (operation) {
|
|
104
110
|
.reserved, .root => unreachable,
|
|
105
111
|
.register => return 0,
|
|
106
|
-
|
|
107
|
-
// TODO: instead of always using the first 32 bytes of the output
|
|
108
|
-
// buffer, get tricky and use a random but deterministic slice
|
|
109
|
-
// of it, filling the rest with 0s.
|
|
110
|
-
.hash => {
|
|
111
|
-
// Fold the input into our current state, creating a hash chain.
|
|
112
|
-
// Hash the input with the client ID since small inputs may collide across clients.
|
|
113
|
-
const client_input = hash(client, input);
|
|
114
|
-
const new_state = hash(state_machine.state, std.mem.asBytes(&client_input));
|
|
115
|
-
|
|
116
|
-
log.debug("state={x} input={x} input.len={} new state={x}", .{
|
|
117
|
-
state_machine.state,
|
|
118
|
-
client_input,
|
|
119
|
-
input.len,
|
|
120
|
-
new_state,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
state_machine.state = new_state;
|
|
124
|
-
std.mem.copy(u8, output, std.mem.asBytes(&state_machine.state));
|
|
125
|
-
return @sizeOf(@TypeOf(state_machine.state));
|
|
126
|
-
},
|
|
112
|
+
.random => return 0,
|
|
127
113
|
}
|
|
128
114
|
}
|
|
129
115
|
|
|
@@ -142,23 +128,13 @@ pub fn StateMachineType(comptime Storage: type) type {
|
|
|
142
128
|
pub fn checkpoint(
|
|
143
129
|
state_machine: *StateMachine,
|
|
144
130
|
callback: fn (*StateMachine) void,
|
|
145
|
-
op: u64,
|
|
146
131
|
) void {
|
|
147
|
-
_ = op;
|
|
148
132
|
assert(state_machine.callback == null);
|
|
149
133
|
assert(state_machine.callback_ticks == 0);
|
|
150
134
|
state_machine.callback = callback;
|
|
151
135
|
state_machine.callback_ticks = state_machine.latency(state_machine.options.checkpoint_mean);
|
|
152
136
|
}
|
|
153
137
|
|
|
154
|
-
pub fn hash(state: u128, input: []const u8) u128 {
|
|
155
|
-
var key: [32]u8 = [_]u8{0} ** 32;
|
|
156
|
-
std.mem.copy(u8, key[0..16], std.mem.asBytes(&state));
|
|
157
|
-
var target: [32]u8 = undefined;
|
|
158
|
-
std.crypto.hash.Blake3.hash(input, &target, .{ .key = key });
|
|
159
|
-
return @bitCast(u128, target[0..16].*);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
138
|
fn latency(state_machine: *StateMachine, mean: u64) u64 {
|
|
163
139
|
return state_machine.prng.random().uintLessThan(u64, 2 * mean);
|
|
164
140
|
}
|