-
Notifications
You must be signed in to change notification settings - Fork 38
/
Copy pathpull_response.zig
148 lines (124 loc) · 4.54 KB
/
pull_response.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
const std = @import("std");
const sig = @import("../sig.zig");
const Hash = sig.core.Hash;
const ArrayList = std.ArrayList;
const KeyPair = std.crypto.sign.Ed25519.KeyPair;
const Pubkey = sig.core.Pubkey;
const RwMux = sig.sync.mux.RwMux;
const GossipTable = sig.gossip.table.GossipTable;
const GossipData = sig.gossip.data.GossipData;
const SignedGossipData = sig.gossip.data.SignedGossipData;
const GossipPullFilter = sig.gossip.pull_request.GossipPullFilter;
const buildGossipPullFilters = sig.gossip.pull_request.buildGossipPullFilters;
const deinitGossipPullFilters = sig.gossip.pull_request.deinitGossipPullFilters;
pub const GOSSIP_PULL_TIMEOUT_MS: u64 = 15000;
pub fn filterSignedGossipDatas(
/// It is advised to use a PRNG, and not a true RNG, otherwise
/// the runtime of this function may be unbounded.
random: std.Random,
allocator: std.mem.Allocator,
gossip_table: *const GossipTable,
filter: *const GossipPullFilter,
caller_wallclock: u64,
max_number_values: usize,
) error{OutOfMemory}!ArrayList(SignedGossipData) {
if (max_number_values == 0) {
return ArrayList(SignedGossipData).init(allocator);
}
const jitter = random.intRangeAtMost(u64, 0, GOSSIP_PULL_TIMEOUT_MS / 4);
const caller_wallclock_with_jitter = caller_wallclock + jitter;
var bloom = filter.filter;
var match_indexs = try gossip_table.getBitmaskMatches(
allocator,
filter.mask,
filter.mask_bits,
);
defer match_indexs.deinit();
const output_size = @min(max_number_values, match_indexs.items.len);
var output = try ArrayList(SignedGossipData).initCapacity(allocator, output_size);
errdefer output.deinit();
for (match_indexs.items) |entry_index| {
var entry = gossip_table.store.iterator().values[entry_index];
// entry is too new
if (entry.value.wallclock() > caller_wallclock_with_jitter) {
continue;
}
// entry is already contained in the bloom
if (bloom.contains(&entry.value_hash.data)) {
continue;
}
// exclude contact info (? not sure why - labs does it)
if (entry.value.data == GossipData.ContactInfo) {
continue;
}
// good
try output.append(entry.value);
if (output.items.len == max_number_values) {
break;
}
}
return output;
}
const LegacyContactInfo = sig.gossip.data.LegacyContactInfo;
test "gossip.pull_response: test filtering values works" {
const gossip_table = try GossipTable.init(std.testing.allocator);
var gossip_table_rw = RwMux(GossipTable).init(gossip_table);
defer {
var lg = gossip_table_rw.write();
lg.mut().deinit();
}
// insert a some value
const kp = try KeyPair.create([_]u8{1} ** 32);
var prng = std.rand.DefaultPrng.init(18);
const random = prng.random();
var lg = gossip_table_rw.write();
for (0..100) |_| {
const gossip_value = SignedGossipData.initRandom(random, &kp);
_ = try lg.mut().insert(gossip_value, 0);
}
lg.unlock();
const max_bytes = 10;
// recver
const failed_pull_hashes = std.ArrayList(Hash).init(std.testing.allocator);
var filters = try buildGossipPullFilters(
std.testing.allocator,
random,
&gossip_table_rw,
&failed_pull_hashes,
max_bytes,
100,
);
defer deinitGossipPullFilters(&filters);
var filter = filters.items[0];
// corresponding value
const pk = kp.public_key;
const id = Pubkey.fromPublicKey(&pk);
var legacy_contact_info = LegacyContactInfo.default(id);
legacy_contact_info.id = id;
legacy_contact_info.wallclock = random.int(u64);
var gossip_value = SignedGossipData.initSigned(&kp, .{
.LegacyContactInfo = legacy_contact_info,
});
// insert more values which the filters should be missing
lg = gossip_table_rw.write();
for (0..64) |_| {
const v2 = SignedGossipData.initRandom(random, &kp);
_ = try lg.mut().insert(v2, 0);
}
const maybe_failing_seed: u64 = random.int(u64);
var maybe_failing_prng = std.Random.Xoshiro256.init(maybe_failing_seed);
var values = try filterSignedGossipDatas(
maybe_failing_prng.random(),
std.testing.allocator,
lg.get(),
&filter,
gossip_value.wallclock(),
100,
);
defer values.deinit();
lg.unlock();
std.testing.expect(values.items.len > 0) catch |err| {
std.log.err("\nThe failing seed is: '{d}'\n", .{maybe_failing_seed});
return err;
};
}