Skip to content

Commit fbc3955

Browse files
julio4lwedge99
authored andcommitted
feat(optimism): flashblock completed sequences (paradigmxyz#18272)
1 parent 87a7999 commit fbc3955

File tree

3 files changed

+74
-18
lines changed

3 files changed

+74
-18
lines changed

crates/optimism/flashblocks/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub use ws::{WsConnect, WsFlashBlockStream};
99

1010
mod payload;
1111
mod sequence;
12+
pub use sequence::FlashBlockCompleteSequence;
1213
mod service;
1314
mod worker;
1415
mod ws;

crates/optimism/flashblocks/src/sequence.rs

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
use crate::{ExecutionPayloadBaseV1, FlashBlock};
22
use alloy_eips::eip2718::WithEncoded;
3+
use eyre::{bail, OptionExt};
34
use reth_primitives_traits::{Recovered, SignedTransaction};
45
use std::collections::BTreeMap;
56
use tracing::trace;
67

78
/// An ordered B-tree keeping the track of a sequence of [`FlashBlock`]s by their indices.
89
#[derive(Debug)]
9-
pub(crate) struct FlashBlockSequence<T> {
10+
pub(crate) struct FlashBlockPendingSequence<T> {
1011
/// tracks the individual flashblocks in order
1112
///
1213
/// With a blocktime of 2s and flashblock tick-rate of 200ms plus one extra flashblock per new
1314
/// pending block, we expect 11 flashblocks per slot.
1415
inner: BTreeMap<u64, PreparedFlashBlock<T>>,
1516
}
1617

17-
impl<T> FlashBlockSequence<T>
18+
impl<T> FlashBlockPendingSequence<T>
1819
where
1920
T: SignedTransaction,
2021
{
@@ -45,16 +46,6 @@ where
4546
Ok(())
4647
}
4748

48-
/// Returns the first block number
49-
pub(crate) fn block_number(&self) -> Option<u64> {
50-
Some(self.inner.values().next()?.block().metadata.block_number)
51-
}
52-
53-
/// Returns the payload base of the first tracked flashblock.
54-
pub(crate) fn payload_base(&self) -> Option<ExecutionPayloadBaseV1> {
55-
self.inner.values().next()?.block().base.clone()
56-
}
57-
5849
/// Iterator over sequence of executable transactions.
5950
///
6051
/// A flashblocks is not ready if there's missing previous flashblocks, i.e. there's a gap in
@@ -74,13 +65,77 @@ where
7465
.flat_map(|(_, block)| block.txs.clone())
7566
}
7667

68+
fn clear(&mut self) {
69+
self.inner.clear();
70+
}
71+
72+
/// Returns the first block number
73+
pub(crate) fn block_number(&self) -> Option<u64> {
74+
Some(self.inner.values().next()?.block().metadata.block_number)
75+
}
76+
77+
/// Returns the payload base of the first tracked flashblock.
78+
pub(crate) fn payload_base(&self) -> Option<ExecutionPayloadBaseV1> {
79+
self.inner.values().next()?.block().base.clone()
80+
}
81+
7782
/// Returns the number of tracked flashblocks.
7883
pub(crate) fn count(&self) -> usize {
7984
self.inner.len()
8085
}
86+
}
8187

82-
fn clear(&mut self) {
83-
self.inner.clear();
88+
/// A complete sequence of flashblocks, often corresponding to a full block.
89+
/// Ensure invariants of a complete flashblocks sequence.
90+
#[derive(Debug)]
91+
pub struct FlashBlockCompleteSequence(Vec<FlashBlock>);
92+
93+
impl FlashBlockCompleteSequence {
94+
/// Create a complete sequence from a vector of flashblocks.
95+
/// Ensure that:
96+
/// * vector is not empty
97+
/// * first flashblock have the base payload
98+
/// * sequence of flashblocks is sound (successive index from 0, same payload id, ...)
99+
pub fn new(blocks: Vec<FlashBlock>) -> eyre::Result<Self> {
100+
let first_block = blocks.first().ok_or_eyre("No flashblocks in sequence")?;
101+
102+
// Ensure that first flashblock have base
103+
first_block.base.as_ref().ok_or_eyre("Flashblock at index 0 has no base")?;
104+
105+
// Ensure that index are successive from 0, have same block number and payload id
106+
if !blocks.iter().enumerate().all(|(idx, block)| {
107+
idx == block.index as usize &&
108+
block.payload_id == first_block.payload_id &&
109+
block.metadata.block_number == first_block.metadata.block_number
110+
}) {
111+
bail!("Flashblock inconsistencies detected in sequence");
112+
}
113+
114+
Ok(Self(blocks))
115+
}
116+
117+
/// Returns the block number
118+
pub fn block_number(&self) -> u64 {
119+
self.0.first().unwrap().metadata.block_number
120+
}
121+
122+
/// Returns the payload base of the first flashblock.
123+
pub fn payload_base(&self) -> &ExecutionPayloadBaseV1 {
124+
self.0.first().unwrap().base.as_ref().unwrap()
125+
}
126+
127+
/// Returns the number of flashblocks in the sequence.
128+
pub const fn count(&self) -> usize {
129+
self.0.len()
130+
}
131+
}
132+
133+
impl<T> TryFrom<FlashBlockPendingSequence<T>> for FlashBlockCompleteSequence {
134+
type Error = eyre::Error;
135+
fn try_from(sequence: FlashBlockPendingSequence<T>) -> Result<Self, Self::Error> {
136+
Self::new(
137+
sequence.inner.into_values().map(|block| block.block().clone()).collect::<Vec<_>>(),
138+
)
84139
}
85140
}
86141

@@ -130,7 +185,7 @@ mod tests {
130185

131186
#[test]
132187
fn test_sequence_stops_before_gap() {
133-
let mut sequence = FlashBlockSequence::new();
188+
let mut sequence = FlashBlockPendingSequence::new();
134189
let tx = EthereumTxEnvelope::new_unhashed(
135190
EthereumTypedTransaction::<TxEip1559>::Eip1559(TxEip1559 {
136191
chain_id: 4,

crates/optimism/flashblocks/src/service.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
sequence::FlashBlockSequence,
2+
sequence::FlashBlockPendingSequence,
33
worker::{BuildArgs, FlashBlockBuilder},
44
ExecutionPayloadBaseV1, FlashBlock,
55
};
@@ -34,7 +34,7 @@ pub struct FlashBlockService<
3434
> {
3535
rx: S,
3636
current: Option<PendingBlock<N>>,
37-
blocks: FlashBlockSequence<N::SignedTx>,
37+
blocks: FlashBlockPendingSequence<N::SignedTx>,
3838
rebuild: bool,
3939
builder: FlashBlockBuilder<EvmConfig, Provider>,
4040
canon_receiver: CanonStateNotifications<N>,
@@ -70,7 +70,7 @@ where
7070
Self {
7171
rx,
7272
current: None,
73-
blocks: FlashBlockSequence::new(),
73+
blocks: FlashBlockPendingSequence::new(),
7474
canon_receiver: provider.subscribe_to_canonical_state(),
7575
builder: FlashBlockBuilder::new(evm_config, provider),
7676
rebuild: false,

0 commit comments

Comments
 (0)