Skip to content

Commit 4dfd671

Browse files
committed
modular raptorcast packet assembly
1 parent aeb5a95 commit 4dfd671

File tree

13 files changed

+2178
-7
lines changed

13 files changed

+2178
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

monad-merkle/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ impl MerkleProof {
3636
siblings,
3737
})
3838
}
39+
3940
pub fn compute_root(&self, leaf: &Hash) -> Option<MerkleHash> {
4041
let mut merkle_hash = hash_to_merkle(leaf);
4142
let mut current_idx = Some(self.tree_leaf_idx as usize);

monad-raptorcast/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ tokio = { workspace = true }
4040

4141
[dev-dependencies]
4242
monad-testutil = { workspace = true }
43+
alloy-primitives = { workspace = true }
4344

4445
clap = { workspace = true, features = ["derive"] }
4546
rstest = { workspace = true }
@@ -53,5 +54,9 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] }
5354
name = "raptor_bench"
5455
harness = false
5556

57+
[[bench]]
58+
name = "encode_bench"
59+
harness = false
60+
5661
[[example]]
5762
name = "service"
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright (C) 2025 Category Labs, Inc.
2+
//
3+
// This program is free software: you can redistribute it and/or modify
4+
// it under the terms of the GNU General Public License as published by
5+
// the Free Software Foundation, either version 3 of the License, or
6+
// (at your option) any later version.
7+
//
8+
// This program is distributed in the hope that it will be useful,
9+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
// GNU General Public License for more details.
12+
//
13+
// You should have received a copy of the GNU General Public License
14+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
16+
use std::{collections::HashMap, net::SocketAddr, str::FromStr};
17+
18+
use bytes::Bytes;
19+
use criterion::{criterion_group, criterion_main, Criterion, Throughput};
20+
use itertools::Itertools as _;
21+
use monad_crypto::certificate_signature::{CertificateSignature, CertificateSignaturePubKey};
22+
use monad_dataplane::udp::DEFAULT_SEGMENT_SIZE;
23+
use monad_raptorcast::{
24+
packet, udp,
25+
util::{BuildTarget, EpochValidators, Redundancy},
26+
};
27+
use monad_secp::SecpSignature;
28+
use monad_testutil::signing::get_key;
29+
use monad_types::{NodeId, Stake};
30+
31+
const NUM_NODES: usize = 100;
32+
33+
pub fn bench(c: &mut Criterion) {
34+
bench_build_messages(c, "Raptorcast 128K", 128 * 1024, "raptorcast");
35+
bench_build_messages(c, "Raptorcast 2M", 2 * 1024 * 1024, "raptorcast");
36+
37+
bench_build_messages(c, "Broadcast 128K", 128 * 1024, "broadcast");
38+
// 2M broadcast yields more than 65535 packets, so we only
39+
// benchmark for 128K.
40+
}
41+
42+
pub fn bench_build_messages(c: &mut Criterion, name: &str, message_size: usize, target: &str) {
43+
let message: Bytes = vec![123_u8; message_size].into();
44+
45+
let mut group = c.benchmark_group(name);
46+
47+
let (author, build_target, known_addrs) = match target {
48+
"raptorcast" => {
49+
group.throughput(Throughput::Bytes(message_size as u64));
50+
setup_raptorcast()
51+
}
52+
"broadcast" => {
53+
group.throughput(Throughput::Bytes((message_size * NUM_NODES) as u64));
54+
setup_broadcast()
55+
}
56+
_ => panic!("unsupported target"),
57+
};
58+
59+
group.bench_function("udp::build_messages", |b| {
60+
b.iter(|| {
61+
let _ = udp::build_messages(
62+
&author,
63+
DEFAULT_SEGMENT_SIZE, // segment_size
64+
message.clone(),
65+
Redundancy::from_u8(2),
66+
0, // epoch_no
67+
0, // unix_ts_ms
68+
build_target.clone(),
69+
&known_addrs,
70+
);
71+
});
72+
});
73+
74+
group.bench_function("packet::build_messages", |b| {
75+
b.iter(|| {
76+
let _ = packet::build_messages(
77+
&author,
78+
DEFAULT_SEGMENT_SIZE, // segment_size
79+
message.clone(),
80+
Redundancy::from_u8(2),
81+
0, // epoch_no
82+
0, // unix_ts_ms
83+
build_target.clone(),
84+
&known_addrs,
85+
&mut rand::thread_rng(),
86+
);
87+
});
88+
});
89+
90+
group.finish();
91+
}
92+
93+
type ST = SecpSignature;
94+
type PT = CertificateSignaturePubKey<ST>;
95+
type KeyPair = <ST as CertificateSignature>::KeyPairType;
96+
97+
fn setup_raptorcast() -> (
98+
KeyPair,
99+
BuildTarget<'static, ST>,
100+
HashMap<NodeId<PT>, SocketAddr>,
101+
) {
102+
let mut keys = (0..(NUM_NODES as u64)).map(get_key::<ST>).collect_vec();
103+
104+
// leak the value to get a 'static reference
105+
let validators = Box::leak(Box::new(EpochValidators {
106+
validators: keys
107+
.iter()
108+
.map(|key| (NodeId::new(key.pubkey()), Stake::ONE))
109+
.collect(),
110+
}));
111+
112+
let addr = SocketAddr::from_str("127.0.0.1:9999").unwrap();
113+
let known_addresses = keys
114+
.iter()
115+
.map(|key| (NodeId::new(key.pubkey()), addr))
116+
.collect();
117+
118+
let author = keys.pop().unwrap();
119+
let epoch_validators = validators.view_without(vec![&NodeId::new(author.pubkey())]);
120+
121+
(
122+
author,
123+
BuildTarget::Raptorcast(epoch_validators),
124+
known_addresses,
125+
)
126+
}
127+
128+
fn setup_broadcast() -> (
129+
KeyPair,
130+
BuildTarget<'static, ST>,
131+
HashMap<NodeId<PT>, SocketAddr>,
132+
) {
133+
let mut keys = (0..100).map(get_key::<ST>).collect_vec();
134+
135+
// leak the value to get a 'static reference
136+
let validators = Box::leak(Box::new(EpochValidators {
137+
validators: keys
138+
.iter()
139+
.map(|key| (NodeId::new(key.pubkey()), Stake::ONE))
140+
.collect(),
141+
}));
142+
143+
let addr = SocketAddr::from_str("127.0.0.1:9999").unwrap();
144+
let known_addresses = keys
145+
.iter()
146+
.map(|key| (NodeId::new(key.pubkey()), addr))
147+
.collect();
148+
149+
let author = keys.pop().unwrap();
150+
let epoch_validators = validators.view_without(vec![&NodeId::new(author.pubkey())]);
151+
152+
(
153+
author,
154+
BuildTarget::Broadcast(epoch_validators.into()),
155+
known_addresses,
156+
)
157+
}
158+
159+
criterion_group!(benches, bench);
160+
criterion_main!(benches);

monad-raptorcast/benches/raptor_bench.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use monad_crypto::hasher::{Hasher, HasherType};
2323
use monad_dataplane::udp::DEFAULT_SEGMENT_SIZE;
2424
use monad_raptor::ManagedDecoder;
2525
use monad_raptorcast::{
26+
packet,
2627
udp::{build_messages, parse_message, MAX_REDUNDANCY, SIGNATURE_CACHE_SIZE},
2728
util::{BuildTarget, EpochValidators, Redundancy},
2829
};
@@ -78,6 +79,49 @@ pub fn criterion_benchmark(c: &mut Criterion) {
7879
});
7980
});
8081

82+
group.bench_function("Encoding (new)", |b| {
83+
let keys = (0_u8..100_u8)
84+
.map(|n| {
85+
let mut hasher = HasherType::new();
86+
hasher.update(n.to_le_bytes());
87+
let mut hash = hasher.hash();
88+
KeyPair::from_bytes(&mut hash.0).unwrap()
89+
})
90+
.collect_vec();
91+
92+
let validators = EpochValidators {
93+
validators: keys
94+
.iter()
95+
.map(|key| (NodeId::new(key.pubkey()), Stake::ONE))
96+
.collect(),
97+
};
98+
99+
let known_addresses = keys
100+
.iter()
101+
.map(|key| {
102+
(
103+
NodeId::new(key.pubkey()),
104+
SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0),
105+
)
106+
})
107+
.collect();
108+
109+
b.iter(|| {
110+
let epoch_validators = validators.view_without(vec![&NodeId::new(keys[0].pubkey())]);
111+
let _ = packet::build_messages::<SecpSignature>(
112+
&keys[0],
113+
DEFAULT_SEGMENT_SIZE, // segment_size
114+
message.clone(),
115+
Redundancy::from_u8(2),
116+
0, // epoch_no
117+
0, // unix_ts_ms
118+
BuildTarget::Raptorcast(epoch_validators),
119+
&known_addresses,
120+
&mut rand::thread_rng(),
121+
);
122+
});
123+
});
124+
81125
group.bench_function("Decoding", |b| {
82126
let keys = (0_u8..100_u8)
83127
.map(|n| {

monad-raptorcast/src/decoding.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1515

1616
#![allow(clippy::manual_range_contains)]
17+
#![allow(clippy::identity_op)]
1718
use std::{
1819
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
1920
hash::Hash,

monad-raptorcast/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ use util::{
6565
pub mod config;
6666
pub mod decoding;
6767
pub mod message;
68+
pub mod packet;
6869
pub mod raptorcast_secondary;
6970
pub mod udp;
7071
pub mod util;

0 commit comments

Comments
 (0)