Skip to content

Commit de26f36

Browse files
committed
Channel tests
1 parent d373c17 commit de26f36

File tree

5 files changed

+221
-7
lines changed

5 files changed

+221
-7
lines changed

crates/derive/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# `kona-derive`
22

3-
> [!WARNING]
4-
> WIP
3+
> **Notice**: This crate is a WIP.
54
65
A `no_std` compatible implementation of the OP Stack's
76
[derivation pipeline](https://specs.optimism.io/protocol/derivation.html#l2-chain-derivation-specification).

crates/derive/src/params.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub const MAX_SPAN_BATCH_BYTES: u64 = MAX_RLP_BYTES_PER_CHANNEL;
1515
/// a channel. This limit is set when decoding the RLP.
1616
pub const MAX_RLP_BYTES_PER_CHANNEL: u64 = 10_000_000;
1717

18+
/// The maximum size of a channel bank.
19+
pub const MAX_CHANNEL_BANK_SIZE: usize = 100_000_000;
20+
1821
/// [CHANNEL_ID_LENGTH] is the length of the channel ID.
1922
pub const CHANNEL_ID_LENGTH: usize = 16;
2023

crates/derive/src/stages/channel_bank.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
33
use alloc::collections::VecDeque;
44
use alloy_primitives::Bytes;
5+
use anyhow::{anyhow, Result};
56
use hashbrown::HashMap;
67

78
use crate::{
8-
params::ChannelID,
9+
params::{ChannelID, MAX_CHANNEL_BANK_SIZE},
910
traits::{ChainProvider, DataAvailabilityProvider},
10-
types::{Channel, RollupConfig},
11+
types::{BlockInfo, Channel, RollupConfig},
1112
};
1213

1314
use super::l1_retrieval::L1Retrieval;
@@ -45,5 +46,39 @@ where
4546
DAP: DataAvailabilityProvider,
4647
CP: ChainProvider,
4748
{
48-
// TODO
49+
/// Create a new [ChannelBank] stage.
50+
pub fn new(cfg: RollupConfig, prev: L1Retrieval<DAP, CP>, chain_provider: CP) -> Self {
51+
Self {
52+
cfg,
53+
channels: HashMap::new(),
54+
channel_queue: VecDeque::new(),
55+
prev,
56+
chain_provider,
57+
}
58+
}
59+
60+
/// Returns the L1 origin [BlockInfo].
61+
pub fn origin(&self) -> Option<&BlockInfo> {
62+
self.prev.origin()
63+
}
64+
65+
/// Prunes the Channel bank, until it is below [MAX_CHANNEL_BANK_SIZE].
66+
pub fn prune(&mut self) -> Result<()> {
67+
// Check total size
68+
let mut total_size = self.channels.iter().fold(0, |acc, (_, c)| acc + c.size());
69+
// Prune until it is reasonable again. The high-priority channel failed to be read,
70+
// so we prune from there.
71+
while total_size > MAX_CHANNEL_BANK_SIZE {
72+
let id = self
73+
.channel_queue
74+
.pop_front()
75+
.ok_or(anyhow!("No channel to prune"))?;
76+
let channel = self
77+
.channels
78+
.remove(&id)
79+
.ok_or(anyhow!("Could not find channel"))?;
80+
total_size -= channel.size();
81+
}
82+
Ok(())
83+
}
4984
}

crates/derive/src/types/channel.rs

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl Channel {
6363
self.id
6464
);
6565
}
66-
if !self.inputs.contains_key(&frame.number) {
66+
if self.inputs.contains_key(&frame.number) {
6767
bail!(
6868
"Frame number already exists in channel. Channel ID: {:?}",
6969
self.id
@@ -138,3 +138,180 @@ impl Channel {
138138
true
139139
}
140140
}
141+
142+
#[cfg(test)]
143+
mod test {
144+
use super::Channel;
145+
use crate::{
146+
params::ChannelID,
147+
types::{BlockInfo, Frame},
148+
};
149+
use alloc::{
150+
string::{String, ToString},
151+
vec,
152+
vec::Vec,
153+
};
154+
155+
extern crate std;
156+
157+
struct FrameValidityTestCase {
158+
name: String,
159+
frames: Vec<Frame>,
160+
should_error: Vec<bool>,
161+
sizes: Vec<u64>,
162+
}
163+
164+
fn run_frame_validity_test(test_case: FrameValidityTestCase) {
165+
let id = [0xFF; 16];
166+
let block = BlockInfo::default();
167+
let mut channel = Channel::new(id, block);
168+
169+
if test_case.frames.len() != test_case.should_error.len()
170+
|| test_case.frames.len() != test_case.sizes.len()
171+
{
172+
panic!("Test case length mismatch");
173+
}
174+
175+
for (i, frame) in test_case.frames.iter().enumerate() {
176+
let result = channel.add_frame(frame.clone(), block);
177+
if test_case.should_error[i] {
178+
assert!(result.is_err());
179+
} else {
180+
assert!(result.is_ok());
181+
}
182+
assert_eq!(channel.size(), test_case.sizes[i] as usize);
183+
}
184+
}
185+
186+
#[test]
187+
fn test_frame_validity() {
188+
let id = [0xFF; 16];
189+
let test_cases = [
190+
FrameValidityTestCase {
191+
name: "wrong channel".to_string(),
192+
frames: vec![Frame {
193+
id: [0xEE; 16],
194+
..Default::default()
195+
}],
196+
should_error: vec![true],
197+
sizes: vec![0],
198+
},
199+
FrameValidityTestCase {
200+
name: "double close".to_string(),
201+
frames: vec![
202+
Frame {
203+
id,
204+
is_last: true,
205+
number: 2,
206+
data: b"four".to_vec(),
207+
},
208+
Frame {
209+
id,
210+
is_last: true,
211+
number: 1,
212+
..Default::default()
213+
},
214+
],
215+
should_error: vec![false, true],
216+
sizes: vec![204, 204],
217+
},
218+
FrameValidityTestCase {
219+
name: "duplicate frame".to_string(),
220+
frames: vec![
221+
Frame {
222+
id,
223+
number: 2,
224+
data: b"four".to_vec(),
225+
..Default::default()
226+
},
227+
Frame {
228+
id,
229+
number: 2,
230+
data: b"seven".to_vec(),
231+
..Default::default()
232+
},
233+
],
234+
should_error: vec![false, true],
235+
sizes: vec![204, 204],
236+
},
237+
FrameValidityTestCase {
238+
name: "duplicate closing frames".to_string(),
239+
frames: vec![
240+
Frame {
241+
id,
242+
number: 2,
243+
is_last: true,
244+
data: b"four".to_vec(),
245+
},
246+
Frame {
247+
id,
248+
number: 2,
249+
is_last: true,
250+
data: b"seven".to_vec(),
251+
},
252+
],
253+
should_error: vec![false, true],
254+
sizes: vec![204, 204],
255+
},
256+
FrameValidityTestCase {
257+
name: "frame past closing".to_string(),
258+
frames: vec![
259+
Frame {
260+
id,
261+
number: 2,
262+
is_last: true,
263+
data: b"four".to_vec(),
264+
},
265+
Frame {
266+
id,
267+
number: 10,
268+
data: b"seven".to_vec(),
269+
..Default::default()
270+
},
271+
],
272+
should_error: vec![false, true],
273+
sizes: vec![204, 204],
274+
},
275+
FrameValidityTestCase {
276+
name: "prune after close frame".to_string(),
277+
frames: vec![
278+
Frame {
279+
id,
280+
number: 10,
281+
is_last: false,
282+
data: b"seven".to_vec(),
283+
},
284+
Frame {
285+
id,
286+
number: 2,
287+
is_last: true,
288+
data: b"four".to_vec(),
289+
},
290+
],
291+
should_error: vec![false, false],
292+
sizes: vec![205, 204],
293+
},
294+
FrameValidityTestCase {
295+
name: "multiple valid frames".to_string(),
296+
frames: vec![
297+
Frame {
298+
id,
299+
number: 10,
300+
data: b"seven__".to_vec(),
301+
..Default::default()
302+
},
303+
Frame {
304+
id,
305+
number: 2,
306+
data: b"four".to_vec(),
307+
..Default::default()
308+
},
309+
],
310+
should_error: vec![false, false],
311+
sizes: vec![207, 411],
312+
},
313+
];
314+
315+
test_cases.into_iter().for_each(run_frame_validity_test);
316+
}
317+
}

crates/derive/src/types/frame.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const MAX_FRAME_LEN: usize = 1000;
1818
/// * frame_data_length = uint32
1919
/// * frame_data = bytes
2020
/// * is_last = bool
21-
#[derive(Debug, Clone, PartialEq, Eq)]
21+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
2222
pub struct Frame {
2323
/// The unique idetifier for the frame.
2424
pub id: ChannelID,

0 commit comments

Comments
 (0)