Skip to content

Commit 81a9701

Browse files
authored
feat(op): minimal jovian support (#84)
* feat: minimal jovian support * do not check sequence tx limits * feat: handle da footprint scalar * apply suggestions
1 parent 793d413 commit 81a9701

File tree

11 files changed

+241
-88
lines changed

11 files changed

+241
-88
lines changed

Cargo.toml

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ test-utils = [
7979
"nanoid",
8080
"tokio/full",
8181
"alloy-genesis",
82-
"rand",
8382
"reth-ipc",
8483
"reth-ethereum/test-utils",
8584
"reth-optimism-rpc/client",
@@ -112,50 +111,48 @@ jsonrpsee = "0.26.0"
112111
parking_lot = "0.12"
113112
metrics = "0.24.0"
114113
priority-queue = "2.0.0"
115-
114+
rand = "0.9"
116115

117116
# Alloy dependencies
118-
alloy-origin = { version = "1.0.37", package = "alloy", features = [
117+
alloy-origin = { version = "1.0.41", package = "alloy", features = [
119118
"k256",
120119
"rpc-types-mev",
121120
] }
122-
alloy-evm = "0.21.2"
123-
alloy-serde = "1.0.37"
121+
alloy-evm = "0.23.0"
124122

125123
# Reth dependencies
126-
reth-origin = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", package = "reth" }
127-
reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
128-
reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
129-
reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
130-
reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
131-
reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
132-
reth-ethereum-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
133-
reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", features = [
124+
reth-origin = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", package = "reth" }
125+
reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
126+
reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
127+
reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
128+
reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
129+
reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
130+
reth-ethereum-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
131+
reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [
134132
"node",
135133
"evm",
136134
] }
137-
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
138-
reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
139-
reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2" }
140-
reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
135+
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
136+
reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
137+
reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
138+
reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
141139

142140
# Reth-optimism dependencies (optional)
143-
op-alloy = { version = "0.20.0", features = ["full"], optional = true }
141+
op-alloy = { version = "0.22.0", features = ["full"], optional = true }
144142
op-alloy-flz = "0.13.1"
145-
reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
146-
reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
147-
reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
148-
reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
149-
reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
150-
reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
151-
reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.8.2", optional = true }
143+
reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
144+
reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
145+
reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
146+
reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
147+
reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
148+
reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
149+
reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true }
152150

153151
# test-utils
154152
rblib-tests-macros = { path = "src/test_utils/macros", optional = true }
155153
jsonrpsee-core = { version = "0.26.0", optional = true, features = ["client"] }
156154
nanoid = { version = "0.4", optional = true }
157155
alloy-genesis = { version = "1.0", default-features = false, optional = true }
158-
rand = { version = "0.9", optional = true }
159156
ctor = { version = "0.5", optional = true }
160157
tracing-subscriber = { version = "0.3", features = [
161158
"env-filter",

src/pipelines/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ pub mod traits {
346346
reth::{
347347
api::FullNodeTypes,
348348
evm::ConfigureEvm,
349+
node::builder::NodeTypes,
349350
providers::{ChainSpecProvider, HeaderProvider, StateProviderFactory},
350351
transaction_pool::{PoolTransaction, TransactionPool},
351352
},
@@ -410,7 +411,11 @@ pub mod traits {
410411

411412
pub trait PlatformExecBounds<P: Platform>:
412413
Platform<
413-
NodeTypes = types::NodeTypes<P>,
414+
NodeTypes: NodeTypes<
415+
ChainSpec = types::ChainSpec<P>,
416+
Primitives = types::Primitives<P>,
417+
Payload = types::PayloadTypes<P>,
418+
>,
414419
EvmConfig = types::EvmConfig<P>,
415420
ExtraLimits = types::ExtraLimits<P>,
416421
>
@@ -419,7 +424,11 @@ pub mod traits {
419424

420425
impl<T, P: Platform> PlatformExecBounds<T> for P where
421426
T: Platform<
422-
NodeTypes = types::NodeTypes<P>,
427+
NodeTypes: NodeTypes<
428+
ChainSpec = types::ChainSpec<P>,
429+
Primitives = types::Primitives<P>,
430+
Payload = types::PayloadTypes<P>,
431+
>,
423432
EvmConfig = types::EvmConfig<P>,
424433
ExtraLimits = types::ExtraLimits<P>,
425434
>

src/platform/ethereum/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,8 @@ where
213213
.map_err(PayloadBuilderError::other)?;
214214

215215
let mut cumulative_gas_used = 0;
216-
let block_gas_limit: u64 = builder.evm_mut().block().gas_limit;
217-
let base_fee = builder.evm_mut().block().basefee;
216+
let block_gas_limit: u64 = builder.evm_mut().block().gas_limit();
217+
let base_fee = builder.evm_mut().block().basefee();
218218

219219
let mut best_txs = best_txs(BestTransactionsAttributes::new(
220220
base_fee,
@@ -277,10 +277,10 @@ where
277277
if is_osaka && estimated_block_size_with_tx > MAX_RLP_BLOCK_SIZE {
278278
best_txs.mark_invalid(
279279
&pool_tx,
280-
InvalidPoolTransactionError::OversizedData(
281-
estimated_block_size_with_tx,
282-
MAX_RLP_BLOCK_SIZE,
283-
),
280+
InvalidPoolTransactionError::OversizedData {
281+
size: estimated_block_size_with_tx,
282+
limit: MAX_RLP_BLOCK_SIZE,
283+
},
284284
);
285285
continue;
286286
}

src/platform/limits.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ pub struct Limits<P: Platform> {
3838
impl<P: Platform> Copy for Limits<P> {}
3939

4040
/// This trait must be implemented by extension limits
41-
pub trait LimitExtension:
42-
Copy + Debug + Default + Send + Sync + 'static
43-
{
41+
pub trait LimitExtension: Default + Copy + Debug + Send + Sync {
4442
#[must_use]
4543
fn clamp(&self, other: &Self) -> Self;
4644
}
@@ -100,6 +98,12 @@ impl<P: Platform> Limits<P> {
10098
self
10199
}
102100

101+
#[must_use]
102+
pub fn with_ext(mut self, ext: P::ExtraLimits) -> Self {
103+
self.ext = ext;
104+
self
105+
}
106+
103107
#[must_use]
104108
pub fn clamp(self, other: &Self) -> Self {
105109
Self {

src/platform/optimism/ext.rs

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,63 @@
11
use {
22
crate::{alloy, prelude::*, reth},
3-
alloy::eips::Encodable2718,
3+
alloy::{
4+
eips::Encodable2718,
5+
evm::{
6+
op_revm::L1BlockInfo,
7+
revm::database::{State, WrapDatabaseRef},
8+
},
9+
},
410
op_alloy_flz,
5-
reth::api::NodeTypes,
11+
reth::{api::NodeTypes, optimism::forks::OpHardforks},
612
};
713

14+
pub trait BlockOpExt<P: Platform> {
15+
/// Returns `true` if [`Ecotone`] hard fork is active at the given block
16+
/// timestamp.
17+
fn is_ecotone_active(&self) -> bool;
18+
19+
/// Returns `true` if [`Holocene`] hard fork is active at the given block
20+
/// timestamp.
21+
fn is_holocene_active(&self) -> bool;
22+
23+
/// Returns `true` if [`Jovian`] hard fork is active at the given block
24+
/// timestamp.
25+
fn is_jovian_active(&self) -> bool;
26+
}
27+
28+
impl<P: Platform> BlockOpExt<P> for BlockContext<P>
29+
where
30+
P: Platform<
31+
NodeTypes: NodeTypes<
32+
ChainSpec = types::ChainSpec<Optimism>,
33+
Payload = types::PayloadTypes<Optimism>,
34+
>,
35+
>,
36+
{
37+
fn is_ecotone_active(&self) -> bool {
38+
self
39+
.chainspec()
40+
.is_ecotone_active_at_timestamp(self.timestamp())
41+
}
42+
43+
fn is_holocene_active(&self) -> bool {
44+
self
45+
.chainspec()
46+
.is_holocene_active_at_timestamp(self.timestamp())
47+
}
48+
49+
fn is_jovian_active(&self) -> bool {
50+
self
51+
.chainspec()
52+
.is_jovian_active_at_timestamp(self.timestamp())
53+
}
54+
}
55+
856
pub trait ExecutionResultOpExt<P: Platform> {
9-
/// Data availability bytes used by this execution.
57+
/// Returns the data availability bytes used by this execution.
58+
/// (`daUsageEstimate`) Only non-deposit transactions are counted towards da
59+
/// bytes usage.
60+
/// Deposit transactions are not counted.
1061
fn da_bytes_used(&self) -> u64;
1162
}
1263

@@ -25,6 +76,7 @@ where
2576
.source
2677
.transactions()
2778
.iter()
79+
.filter(|tx| !tx.is_deposit())
2880
.map(|tx| {
2981
op_alloy_flz::tx_estimated_size_fjord_bytes(
3082
tx.encoded_2718().as_slice(),
@@ -35,9 +87,13 @@ where
3587
}
3688

3789
pub trait SpanOpExt<P: Platform> {
38-
/// Returns the total data availability bytes used by all checkpoints in this
39-
/// span
90+
/// Returns the cumulative data availability bytes used by all checkpoints in
91+
/// this span
4092
fn da_bytes_used(&self) -> u64;
93+
94+
/// Returns the cumulative data availability footprint used by all checkpoints
95+
/// in this span
96+
fn da_footprint(&self) -> u64;
4197
}
4298

4399
impl<P: Platform> SpanOpExt<P> for Span<P>
@@ -53,6 +109,10 @@ where
53109
fn da_bytes_used(&self) -> u64 {
54110
self.iter().map(CheckpointOpExt::da_bytes_used).sum()
55111
}
112+
113+
fn da_footprint(&self) -> u64 {
114+
self.iter().filter_map(CheckpointOpExt::da_footprint).sum()
115+
}
56116
}
57117

58118
pub trait CheckpointOpExt<P: Platform> {
@@ -62,6 +122,27 @@ pub trait CheckpointOpExt<P: Platform> {
62122
/// Returns the cumulative data availability bytes used by all checkpoints in
63123
/// the history of this checkpoint, including this checkpoint itself.
64124
fn cumulative_da_bytes_used(&self) -> u64;
125+
126+
/// Returns the data availability footprint gas scalar (L1 Block Attribute)
127+
fn da_footprint_gas_scalar(&self) -> Option<u16>;
128+
129+
/// Returns the data availability footprint
130+
fn da_footprint(&self) -> Option<u64> {
131+
self
132+
.da_footprint_gas_scalar()
133+
.map(|scalar| self.da_bytes_used() * u64::from(scalar))
134+
}
135+
136+
/// Returns the cumulative data availability footprint used by all checkpoints
137+
/// in the history of this checkpoint, including this checkpoint itself.
138+
fn cumulative_da_footprint(&self) -> Option<u64>;
139+
140+
/// Returns the blob fields for the header.
141+
///
142+
/// After jovian: this will return `da_footprint`.
143+
/// After Ecotone: this will always return Some(0) (blobs aren't supported)
144+
/// Pre-Ecotone: these fields aren't used.
145+
fn blob_fields(&self) -> (Option<u64>, Option<u64>);
65146
}
66147

67148
impl<P: Platform> CheckpointOpExt<P> for Checkpoint<P>
@@ -81,4 +162,40 @@ where
81162
fn cumulative_da_bytes_used(&self) -> u64 {
82163
self.history().da_bytes_used()
83164
}
165+
166+
fn da_footprint_gas_scalar(&self) -> Option<u16> {
167+
self.block().is_jovian_active().then(|| {
168+
let mut state = State::builder()
169+
.with_database(WrapDatabaseRef(self))
170+
.build();
171+
// fetch from storage. It could be memoized as it's set once at the start
172+
// of the block
173+
L1BlockInfo::fetch_da_footprint_gas_scalar(&mut state).ok()
174+
})?
175+
}
176+
177+
fn cumulative_da_footprint(&self) -> Option<u64> {
178+
self
179+
.block()
180+
.is_jovian_active()
181+
.then(|| self.history().da_footprint())
182+
}
183+
184+
fn blob_fields(&self) -> (Option<u64>, Option<u64>) {
185+
// Jovian
186+
if self.block().is_jovian_active() {
187+
let footprint = self
188+
.cumulative_da_footprint()
189+
.expect("Jovian but no da footprint");
190+
(Some(0), Some(footprint))
191+
}
192+
// Ecotone
193+
else if self.block().is_ecotone_active() {
194+
(Some(0), Some(0))
195+
}
196+
// Pre-Ecotone
197+
else {
198+
(None, None)
199+
}
200+
}
84201
}

0 commit comments

Comments
 (0)