@push.rocks/smartnftables 2.5.2 → 3.0.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/changelog.md +31 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +5 -5
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/managed.egress.types.d.ts +69 -3
- package/package.json +6 -6
- package/readme.md +342 -61
- package/rust/src/egress.compile.rs +157 -76
- package/rust/src/egress.host.rs +104 -44
- package/rust/src/egress.hostgrant.rs +14 -14
- package/rust/src/egress.keys.rs +305 -0
- package/rust/src/egress.leased.rs +399 -0
- package/rust/src/egress.poolguard.rs +45 -0
- package/rust/src/egress.private.rs +197 -0
- package/rust/src/egress.published.rs +370 -0
- package/rust/src/egress.router.rs +64 -295
- package/rust/src/egress.rs +248 -17
- package/rust/src/egress.workloadgrant.rs +88 -0
- package/rust/src/egress_hostgrant_tests.rs +196 -47
- package/rust/src/egress_localplatform_tests.rs +204 -0
- package/rust/src/egress_localport_tests.rs +266 -0
- package/rust/src/egress_publishedrange_tests.rs +812 -0
- package/rust/src/egress_routerequivalence_tests.rs +448 -0
- package/rust/src/egress_tests.rs +132 -230
- package/rust/src/egress_workloadgrant_tests.rs +313 -0
- package/rust/src/owner.rs +77 -15
- package/rust/src/owner_host_traffic_tests.rs +4 -0
- package/rust/src/owner_hostgrant_traffic_tests.rs +9 -4
- package/rust/src/owner_identity_tests.rs +228 -80
- package/rust/src/owner_localplatform_tests.rs +274 -0
- package/rust/src/owner_localport_tests.rs +241 -0
- package/rust/src/owner_publishedrange_traffic_tests.rs +306 -0
- package/rust/src/owner_router_traffic_tests.rs +2 -0
- package/rust/src/owner_scale_traffic_tests.rs +383 -0
- package/rust/src/owner_tests.rs +15 -0
- package/rust/src/owner_workloadgrant_tests.rs +278 -0
- package/rust/src/policy.rs +29 -25
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/managed.egress.types.ts +71 -3
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
//! Router decision corpus: every hook's verdict, stored zone and NAT for a
|
|
2
|
+
//! deterministic corpus of packets around each fixture's grants, rules and
|
|
3
|
+
//! protection. The digests were frozen from the per-rule router compiler, so the
|
|
4
|
+
//! set-backed compiler must decide every one of these packets identically.
|
|
5
|
+
use super::hostgrant_tests::{Flow, Simulator};
|
|
6
|
+
use super::{Destination, Policy, Prepared, RouterScope, Scope};
|
|
7
|
+
use serde_json::{json, Value};
|
|
8
|
+
|
|
9
|
+
const NEW: u32 = 8;
|
|
10
|
+
const ESTABLISHED: u32 = 2;
|
|
11
|
+
const SYN: u8 = 0x02;
|
|
12
|
+
const ACK: u8 = 0x10;
|
|
13
|
+
const CHAINS: [&str; 7] = [
|
|
14
|
+
"raw_in", "raw_out", "input", "forward", "output", "post", "pre",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/// A small deterministic generator; the corpus must never change between runs.
|
|
18
|
+
struct Random(u64);
|
|
19
|
+
impl Random {
|
|
20
|
+
fn next(&mut self) -> u64 {
|
|
21
|
+
self.0 ^= self.0 << 13;
|
|
22
|
+
self.0 ^= self.0 >> 7;
|
|
23
|
+
self.0 ^= self.0 << 17;
|
|
24
|
+
self.0
|
|
25
|
+
}
|
|
26
|
+
fn pick<T: Copy>(&mut self, items: &[T]) -> T {
|
|
27
|
+
items[(self.next() % items.len() as u64) as usize]
|
|
28
|
+
}
|
|
29
|
+
fn chance(&mut self, percent: u64) -> bool {
|
|
30
|
+
self.next() % 100 < percent
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type Link = Option<(u32, &'static str)>;
|
|
35
|
+
fn leak(value: String) -> &'static str {
|
|
36
|
+
Box::leak(value.into_boxed_str())
|
|
37
|
+
}
|
|
38
|
+
fn first(prefix: &str) -> [u8; 4] {
|
|
39
|
+
let (address, _) = super::prefix(prefix).unwrap();
|
|
40
|
+
address.to_be_bytes()
|
|
41
|
+
}
|
|
42
|
+
fn last(prefix: &str) -> [u8; 4] {
|
|
43
|
+
let (address, mask) = super::prefix(prefix).unwrap();
|
|
44
|
+
(address | !mask).to_be_bytes()
|
|
45
|
+
}
|
|
46
|
+
fn ip(value: &str) -> [u8; 4] {
|
|
47
|
+
value.parse::<std::net::Ipv4Addr>().unwrap().octets()
|
|
48
|
+
}
|
|
49
|
+
fn label(value: &str) -> [u8; 16] {
|
|
50
|
+
let bytes: Vec<u8> = (0..16)
|
|
51
|
+
.map(|index| u8::from_str_radix(&value[index * 2..index * 2 + 2], 16).unwrap())
|
|
52
|
+
.collect();
|
|
53
|
+
bytes.try_into().unwrap()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// The values a fixture's packets are drawn from, and templates of the flows its
|
|
57
|
+
/// authority names, in both directions.
|
|
58
|
+
struct Pools {
|
|
59
|
+
links: Vec<Link>,
|
|
60
|
+
addresses: Vec<[u8; 4]>,
|
|
61
|
+
ports: Vec<u16>,
|
|
62
|
+
zones: Vec<u16>,
|
|
63
|
+
labels: Vec<[u8; 16]>,
|
|
64
|
+
templates: Vec<Flow>,
|
|
65
|
+
}
|
|
66
|
+
fn flow(
|
|
67
|
+
input: Link,
|
|
68
|
+
output: Link,
|
|
69
|
+
protocol: u8,
|
|
70
|
+
source: ([u8; 4], u16),
|
|
71
|
+
destination: ([u8; 4], u16),
|
|
72
|
+
) -> Flow {
|
|
73
|
+
Flow {
|
|
74
|
+
input,
|
|
75
|
+
output,
|
|
76
|
+
protocol,
|
|
77
|
+
source: source.0,
|
|
78
|
+
destination: destination.0,
|
|
79
|
+
source_port: source.1,
|
|
80
|
+
destination_port: destination.1,
|
|
81
|
+
tcp_flags: SYN,
|
|
82
|
+
state: NEW,
|
|
83
|
+
reply: false,
|
|
84
|
+
zone: 0,
|
|
85
|
+
original: (source.0, destination.0, source.1, destination.1),
|
|
86
|
+
uid: None,
|
|
87
|
+
label: [0; 16],
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
fn answer(flow: Flow) -> Flow {
|
|
91
|
+
Flow {
|
|
92
|
+
input: flow.output,
|
|
93
|
+
output: flow.input,
|
|
94
|
+
source: flow.destination,
|
|
95
|
+
destination: flow.source,
|
|
96
|
+
source_port: flow.destination_port,
|
|
97
|
+
destination_port: flow.source_port,
|
|
98
|
+
tcp_flags: ACK,
|
|
99
|
+
state: ESTABLISHED,
|
|
100
|
+
reply: true,
|
|
101
|
+
..flow
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
fn protocol(name: &str) -> u8 {
|
|
105
|
+
if name == "tcp" {
|
|
106
|
+
6
|
|
107
|
+
} else {
|
|
108
|
+
17
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
fn pools(scope: &RouterScope) -> Pools {
|
|
112
|
+
let link = |index: u32, name: &str| Some((index, leak(name.to_string())));
|
|
113
|
+
let mut links: Vec<Link> = vec![
|
|
114
|
+
None,
|
|
115
|
+
link(scope.handoff.interface_index, &scope.handoff.interface_name),
|
|
116
|
+
];
|
|
117
|
+
for item in &scope.links {
|
|
118
|
+
links.push(link(item.interface_index, &item.interface_name));
|
|
119
|
+
// The same index under another name, and the same name at another index.
|
|
120
|
+
links.push(link(item.interface_index, "renamed"));
|
|
121
|
+
links.push(link(item.interface_index + 100, &item.interface_name));
|
|
122
|
+
}
|
|
123
|
+
let endpoint_link = |id: &str| {
|
|
124
|
+
let endpoint = scope.endpoints.iter().find(|item| item.id == id).unwrap();
|
|
125
|
+
link(endpoint.interface_index, &endpoint.interface_name)
|
|
126
|
+
};
|
|
127
|
+
let handoff = link(scope.handoff.interface_index, &scope.handoff.interface_name);
|
|
128
|
+
let mut addresses = vec![
|
|
129
|
+
ip("198.51.100.7"),
|
|
130
|
+
ip("8.8.8.8"),
|
|
131
|
+
ip("10.9.9.9"),
|
|
132
|
+
ip("192.0.2.200"),
|
|
133
|
+
];
|
|
134
|
+
for item in scope.links.iter().chain(std::iter::once(&scope.handoff)) {
|
|
135
|
+
addresses.extend(item.required_ipv4_addresses.iter().map(|value| ip(value)));
|
|
136
|
+
}
|
|
137
|
+
for endpoint in &scope.endpoints {
|
|
138
|
+
for prefix in &endpoint.source_prefixes {
|
|
139
|
+
addresses.extend([first(prefix), last(prefix)]);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for prefix in &scope.protection.prefixes {
|
|
143
|
+
addresses.extend([first(prefix), last(prefix)]);
|
|
144
|
+
}
|
|
145
|
+
for endpoint in &scope.protection.platform_endpoints {
|
|
146
|
+
addresses.push(ip(&endpoint.address));
|
|
147
|
+
}
|
|
148
|
+
let mut ports = vec![53, 443, 3000, 5353, 8080, 40000, 65535];
|
|
149
|
+
let mut zones = vec![0, 7];
|
|
150
|
+
let mut labels = vec![[0; 16], [0x55; 16]];
|
|
151
|
+
let mut templates = Vec::new();
|
|
152
|
+
for generation in &scope.generations {
|
|
153
|
+
zones.push(generation.conntrack_zone);
|
|
154
|
+
labels.push(label(&generation.conntrack_label));
|
|
155
|
+
let transit = ip(&generation.transit_source_address);
|
|
156
|
+
for range in &generation.source_port_ranges {
|
|
157
|
+
ports.extend([
|
|
158
|
+
range.first,
|
|
159
|
+
range.last,
|
|
160
|
+
range.first.saturating_sub(1),
|
|
161
|
+
range.last.saturating_add(1),
|
|
162
|
+
]);
|
|
163
|
+
}
|
|
164
|
+
for grant in &generation.grants {
|
|
165
|
+
let target = match &grant.destination {
|
|
166
|
+
Destination::Public { prefix } => {
|
|
167
|
+
if prefix == "0.0.0.0/0" {
|
|
168
|
+
ip("198.51.100.7")
|
|
169
|
+
} else {
|
|
170
|
+
first(prefix)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
Destination::Platform { endpoint_id } => ip(&scope
|
|
174
|
+
.protection
|
|
175
|
+
.platform_endpoints
|
|
176
|
+
.iter()
|
|
177
|
+
.find(|item| item.id == *endpoint_id)
|
|
178
|
+
.unwrap()
|
|
179
|
+
.address),
|
|
180
|
+
};
|
|
181
|
+
let source = first(&grant.source_prefix);
|
|
182
|
+
let input = grant
|
|
183
|
+
.source_endpoint
|
|
184
|
+
.as_deref()
|
|
185
|
+
.map(endpoint_link)
|
|
186
|
+
.unwrap_or(None);
|
|
187
|
+
let original = flow(
|
|
188
|
+
input,
|
|
189
|
+
handoff,
|
|
190
|
+
protocol(&grant.protocol),
|
|
191
|
+
(source, grant.source_port.unwrap_or(40000)),
|
|
192
|
+
(target, grant.destination_port.unwrap_or(443)),
|
|
193
|
+
);
|
|
194
|
+
let zoned = Flow {
|
|
195
|
+
zone: generation.conntrack_zone,
|
|
196
|
+
..original
|
|
197
|
+
};
|
|
198
|
+
let labelled = Flow {
|
|
199
|
+
label: label(&generation.conntrack_label),
|
|
200
|
+
..zoned
|
|
201
|
+
};
|
|
202
|
+
templates.extend([original, zoned, labelled, answer(labelled)]);
|
|
203
|
+
// The translated return arriving on the handoff, before conntrack.
|
|
204
|
+
templates.push(flow(
|
|
205
|
+
handoff,
|
|
206
|
+
None,
|
|
207
|
+
protocol(&grant.protocol),
|
|
208
|
+
(target, grant.destination_port.unwrap_or(443)),
|
|
209
|
+
(transit, grant.source_port_range.first),
|
|
210
|
+
));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for rule in &scope.rules {
|
|
214
|
+
let input = rule
|
|
215
|
+
.source_endpoint
|
|
216
|
+
.as_deref()
|
|
217
|
+
.map(endpoint_link)
|
|
218
|
+
.unwrap_or(None);
|
|
219
|
+
let output = rule
|
|
220
|
+
.destination_endpoint
|
|
221
|
+
.as_deref()
|
|
222
|
+
.map(endpoint_link)
|
|
223
|
+
.unwrap_or(None);
|
|
224
|
+
let protocols: Vec<u8> = if rule.protocol == "any" {
|
|
225
|
+
vec![6, 17, 1]
|
|
226
|
+
} else {
|
|
227
|
+
vec![protocol(&rule.protocol)]
|
|
228
|
+
};
|
|
229
|
+
for value in protocols {
|
|
230
|
+
let template = flow(
|
|
231
|
+
input,
|
|
232
|
+
output,
|
|
233
|
+
value,
|
|
234
|
+
(
|
|
235
|
+
first(&rule.source_prefix),
|
|
236
|
+
rule.source_port.unwrap_or(40000),
|
|
237
|
+
),
|
|
238
|
+
(
|
|
239
|
+
first(&rule.destination_prefix),
|
|
240
|
+
rule.destination_port.unwrap_or(40001),
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
templates.extend([template, answer(template)]);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for grant in scope.host_grants.iter().chain(&scope.workload_grants) {
|
|
247
|
+
ports.push(grant.destination_port);
|
|
248
|
+
let template = flow(
|
|
249
|
+
handoff,
|
|
250
|
+
links[3],
|
|
251
|
+
protocol(&grant.protocol),
|
|
252
|
+
(ip(&grant.source_address), 40000),
|
|
253
|
+
(ip(&grant.destination_address), grant.destination_port),
|
|
254
|
+
);
|
|
255
|
+
templates.extend([template, answer(template)]);
|
|
256
|
+
}
|
|
257
|
+
Pools {
|
|
258
|
+
links,
|
|
259
|
+
addresses,
|
|
260
|
+
ports,
|
|
261
|
+
zones,
|
|
262
|
+
labels,
|
|
263
|
+
templates,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/// A template changed in up to three operands, or an arbitrary packet.
|
|
267
|
+
fn packet(random: &mut Random, pools: &Pools) -> Flow {
|
|
268
|
+
let mut flow = if random.chance(80) {
|
|
269
|
+
random.pick(&pools.templates)
|
|
270
|
+
} else {
|
|
271
|
+
flow(
|
|
272
|
+
random.pick(&pools.links),
|
|
273
|
+
random.pick(&pools.links),
|
|
274
|
+
random.pick(&[6, 17]),
|
|
275
|
+
(random.pick(&pools.addresses), random.pick(&pools.ports)),
|
|
276
|
+
(random.pick(&pools.addresses), random.pick(&pools.ports)),
|
|
277
|
+
)
|
|
278
|
+
};
|
|
279
|
+
for _ in 0..random.next() % 4 {
|
|
280
|
+
match random.next() % 14 {
|
|
281
|
+
0 => flow.input = random.pick(&pools.links),
|
|
282
|
+
1 => flow.output = random.pick(&pools.links),
|
|
283
|
+
2 => flow.source = random.pick(&pools.addresses),
|
|
284
|
+
3 => flow.destination = random.pick(&pools.addresses),
|
|
285
|
+
4 => flow.source_port = random.pick(&pools.ports),
|
|
286
|
+
5 => flow.destination_port = random.pick(&pools.ports),
|
|
287
|
+
6 => flow.protocol = random.pick(&[6, 17, 1]),
|
|
288
|
+
7 => flow.state = random.pick(&[NEW, ESTABLISHED]),
|
|
289
|
+
8 => flow.reply = !flow.reply,
|
|
290
|
+
9 => flow.zone = random.pick(&pools.zones),
|
|
291
|
+
10 => flow.label = random.pick(&pools.labels),
|
|
292
|
+
11 => flow.tcp_flags = random.pick(&[SYN, ACK, SYN | ACK, 0x04, 0x42]),
|
|
293
|
+
12 => {
|
|
294
|
+
flow.original.1 = random.pick(&pools.addresses);
|
|
295
|
+
flow.original.3 = random.pick(&pools.ports);
|
|
296
|
+
}
|
|
297
|
+
_ => {
|
|
298
|
+
flow.original.0 = random.pick(&pools.addresses);
|
|
299
|
+
flow.original.2 = random.pick(&pools.ports);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
flow
|
|
304
|
+
}
|
|
305
|
+
/// Linux runs each hook with exactly these interfaces: prerouting and input with
|
|
306
|
+
/// an incoming one only, output with an outgoing one only, forward with both, and
|
|
307
|
+
/// postrouting with an outgoing one.
|
|
308
|
+
fn possible(chain: &str, flow: &Flow) -> bool {
|
|
309
|
+
let (incoming, outgoing) = (flow.input.is_some(), flow.output.is_some());
|
|
310
|
+
match chain {
|
|
311
|
+
"raw_in" | "pre" | "input" => incoming && !outgoing,
|
|
312
|
+
"raw_out" | "output" => !incoming && outgoing,
|
|
313
|
+
"forward" => incoming && outgoing,
|
|
314
|
+
_ => outgoing,
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
fn prepared(value: Value) -> Prepared {
|
|
318
|
+
serde_json::from_value::<Policy>(value)
|
|
319
|
+
.unwrap()
|
|
320
|
+
.prepare()
|
|
321
|
+
.unwrap()
|
|
322
|
+
}
|
|
323
|
+
/// Every decision of the corpus, as `chain verdict zone translation` lines.
|
|
324
|
+
fn decisions(prepared: &Prepared, seed: u64, count: usize) -> Vec<String> {
|
|
325
|
+
let Scope::RouterEgress(scope) = &prepared.policy.scope else {
|
|
326
|
+
panic!("router fixture")
|
|
327
|
+
};
|
|
328
|
+
let pools = pools(scope);
|
|
329
|
+
let simulator = Simulator::new(prepared);
|
|
330
|
+
let mut random = Random(seed);
|
|
331
|
+
let mut lines = Vec::new();
|
|
332
|
+
for _ in 0..count {
|
|
333
|
+
let flow = packet(&mut random, &pools);
|
|
334
|
+
for chain in CHAINS {
|
|
335
|
+
if !simulator.has(chain) || !possible(chain, &flow) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
// Postrouting only ever sees what forward or output let through.
|
|
339
|
+
let filter = if flow.input.is_some() {
|
|
340
|
+
"forward"
|
|
341
|
+
} else {
|
|
342
|
+
"output"
|
|
343
|
+
};
|
|
344
|
+
if chain == "post" && simulator.decide(filter, &flow).verdict != 1 {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
let decision = simulator.decide(chain, &flow);
|
|
348
|
+
lines.push(format!(
|
|
349
|
+
"{chain} {} {} {} {:?}",
|
|
350
|
+
decision.verdict,
|
|
351
|
+
decision.chain == "policy",
|
|
352
|
+
decision.zone,
|
|
353
|
+
decision.translation
|
|
354
|
+
));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
lines
|
|
358
|
+
}
|
|
359
|
+
fn two_generations() -> Value {
|
|
360
|
+
let mut value = super::tests::router();
|
|
361
|
+
let mut second = value["scope"]["generations"][0].clone();
|
|
362
|
+
second["lease"]["id"] = json!("lease-b");
|
|
363
|
+
second["transitSourceAddress"] = json!("10.240.0.2");
|
|
364
|
+
second["conntrackZone"] = json!(2);
|
|
365
|
+
second["conntrackLabel"] = json!("b".repeat(32));
|
|
366
|
+
second["sourcePortRanges"] = json!([{"protocol":"tcp","first":11000,"last":11999},
|
|
367
|
+
{"protocol":"udp","first":11000,"last":11999}]);
|
|
368
|
+
second["grants"] = json!([
|
|
369
|
+
{"sourceEndpoint":"work","sourcePrefix":"10.241.0.2/32","destination":{"kind":"public","prefix":"198.51.100.0/24"},
|
|
370
|
+
"protocol":"udp","sourcePort":null,"destinationPort":5353,"sourcePortRange":{"first":11000,"last":11999}}
|
|
371
|
+
]);
|
|
372
|
+
value["scope"]["generations"]
|
|
373
|
+
.as_array_mut()
|
|
374
|
+
.unwrap()
|
|
375
|
+
.push(second);
|
|
376
|
+
value
|
|
377
|
+
}
|
|
378
|
+
fn fixtures() -> Vec<(&'static str, Value)> {
|
|
379
|
+
let grants = super::hostgrant_tests::granted(
|
|
380
|
+
super::workloadgrant_tests::granted(
|
|
381
|
+
super::workloadgrant_tests::two_workloads(),
|
|
382
|
+
json!([super::workloadgrant_tests::workload_grant(
|
|
383
|
+
"tcp",
|
|
384
|
+
"10.241.0.2",
|
|
385
|
+
"10.241.1.2",
|
|
386
|
+
8080
|
|
387
|
+
)]),
|
|
388
|
+
),
|
|
389
|
+
json!([super::hostgrant_tests::host_grant(
|
|
390
|
+
"tcp",
|
|
391
|
+
"10.240.0.1",
|
|
392
|
+
"10.241.0.2",
|
|
393
|
+
8080
|
|
394
|
+
)]),
|
|
395
|
+
);
|
|
396
|
+
let (node, _) = super::publishedrange_tests::node(3, true, 0, 0, false);
|
|
397
|
+
vec![
|
|
398
|
+
("router", super::tests::router()),
|
|
399
|
+
(
|
|
400
|
+
"combined_capacity",
|
|
401
|
+
super::tests::combined_router_capacity(),
|
|
402
|
+
),
|
|
403
|
+
("two_generations", two_generations()),
|
|
404
|
+
("grants", grants),
|
|
405
|
+
("pallet_3", node),
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
#[test]
|
|
410
|
+
fn router_decisions_match_the_frozen_per_rule_corpus() {
|
|
411
|
+
use sha2::{Digest, Sha256};
|
|
412
|
+
let mut summary = Vec::new();
|
|
413
|
+
for (name, value) in fixtures() {
|
|
414
|
+
let lines = decisions(&prepared(value), 0x9e37_79b9_7f4a_7c15, 16000);
|
|
415
|
+
let accepted = lines
|
|
416
|
+
.iter()
|
|
417
|
+
.filter(|line| {
|
|
418
|
+
line.split(' ').nth(1) == Some("1") && line.split(' ').nth(2) == Some("false")
|
|
419
|
+
})
|
|
420
|
+
.count();
|
|
421
|
+
let digest = format!("{:x}", Sha256::digest(lines.join("\n")));
|
|
422
|
+
if let Ok(directory) = std::env::var("SMARTNFTABLES_CORPUS_DIR") {
|
|
423
|
+
std::fs::write(format!("{directory}/{name}.txt"), lines.join("\n")).unwrap();
|
|
424
|
+
}
|
|
425
|
+
println!(
|
|
426
|
+
"ROUTER_CORPUS {name} decisions={} accepted={accepted} sha256={digest}",
|
|
427
|
+
lines.len()
|
|
428
|
+
);
|
|
429
|
+
summary.push(format!("{name} {digest}"));
|
|
430
|
+
}
|
|
431
|
+
assert_eq!(
|
|
432
|
+
summary,
|
|
433
|
+
FROZEN
|
|
434
|
+
.iter()
|
|
435
|
+
.map(|item| item.to_string())
|
|
436
|
+
.collect::<Vec<_>>()
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
/// Frozen from the per-rule router compiler of 2.6.0 (with this change's
|
|
440
|
+
/// simulator, which models hooks, interval sets, maps and inverted lookups), and
|
|
441
|
+
/// reproduced unchanged by the set-backed compiler.
|
|
442
|
+
const FROZEN: [&str; 5] = [
|
|
443
|
+
"router d9a005d2c0ce7ceb6b6a6de91ff087fc4802c6fb2100a9a9ea7ad9e01cc131db",
|
|
444
|
+
"combined_capacity c2c055acc2b7c68570454289a6c150dbb29a9dd1de3b3fecda79b096430900f4",
|
|
445
|
+
"two_generations b6750e4930be4cb2713a812d56624f410447c0d57eeb0111136054b43e48d814",
|
|
446
|
+
"grants 434776a2946b64fd27fb595f792a96cbdb6f6684d2ace15f57cdaec22eee6557",
|
|
447
|
+
"pallet_3 7d2d06280b903bbfba712eb1a55d62270f0c0b473597b5940fcb4d91b0d5c325",
|
|
448
|
+
];
|