Skip to content

Commit 6e36594

Browse files
authored
feat: allow customizing dev block timestamp (#19904)
1 parent e15b404 commit 6e36594

File tree

7 files changed

+77
-54
lines changed

7 files changed

+77
-54
lines changed

Cargo.lock

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

crates/engine/local/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ reth-engine-primitives = { workspace = true, features = ["std"] }
1515
reth-ethereum-engine-primitives.workspace = true
1616
reth-payload-builder.workspace = true
1717
reth-payload-primitives.workspace = true
18+
reth-primitives-traits.workspace = true
1819
reth-storage-api.workspace = true
1920
reth-transaction-pool.workspace = true
2021

@@ -43,4 +44,5 @@ op = [
4344
"dep:op-alloy-rpc-types-engine",
4445
"dep:reth-optimism-chainspec",
4546
"reth-payload-primitives/op",
47+
"reth-primitives-traits/op",
4648
]

crates/engine/local/src/miner.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//! Contains the implementation of the mining mode for the local engine.
22
3-
use alloy_consensus::BlockHeader;
43
use alloy_primitives::{TxHash, B256};
54
use alloy_rpc_types_engine::ForkchoiceState;
65
use eyre::OptionExt;
@@ -10,14 +9,15 @@ use reth_payload_builder::PayloadBuilderHandle;
109
use reth_payload_primitives::{
1110
BuiltPayload, EngineApiMessageVersion, PayloadAttributesBuilder, PayloadKind, PayloadTypes,
1211
};
12+
use reth_primitives_traits::{HeaderTy, SealedHeaderFor};
1313
use reth_storage_api::BlockReader;
1414
use reth_transaction_pool::TransactionPool;
1515
use std::{
1616
collections::VecDeque,
1717
future::Future,
1818
pin::Pin,
1919
task::{Context, Poll},
20-
time::{Duration, UNIX_EPOCH},
20+
time::Duration,
2121
};
2222
use tokio::time::Interval;
2323
use tokio_stream::wrappers::ReceiverStream;
@@ -106,36 +106,39 @@ pub struct LocalMiner<T: PayloadTypes, B, Pool: TransactionPool + Unpin> {
106106
mode: MiningMode<Pool>,
107107
/// The payload builder for the engine
108108
payload_builder: PayloadBuilderHandle<T>,
109-
/// Timestamp for the next block.
110-
last_timestamp: u64,
109+
/// Latest block in the chain so far.
110+
last_header: SealedHeaderFor<<T::BuiltPayload as BuiltPayload>::Primitives>,
111111
/// Stores latest mined blocks.
112112
last_block_hashes: VecDeque<B256>,
113113
}
114114

115115
impl<T, B, Pool> LocalMiner<T, B, Pool>
116116
where
117117
T: PayloadTypes,
118-
B: PayloadAttributesBuilder<<T as PayloadTypes>::PayloadAttributes>,
118+
B: PayloadAttributesBuilder<
119+
T::PayloadAttributes,
120+
HeaderTy<<T::BuiltPayload as BuiltPayload>::Primitives>,
121+
>,
119122
Pool: TransactionPool + Unpin,
120123
{
121124
/// Spawns a new [`LocalMiner`] with the given parameters.
122125
pub fn new(
123-
provider: impl BlockReader,
126+
provider: impl BlockReader<Header = HeaderTy<<T::BuiltPayload as BuiltPayload>::Primitives>>,
124127
payload_attributes_builder: B,
125128
to_engine: ConsensusEngineHandle<T>,
126129
mode: MiningMode<Pool>,
127130
payload_builder: PayloadBuilderHandle<T>,
128131
) -> Self {
129-
let latest_header =
132+
let last_header =
130133
provider.sealed_header(provider.best_block_number().unwrap()).unwrap().unwrap();
131134

132135
Self {
133136
payload_attributes_builder,
134137
to_engine,
135138
mode,
136139
payload_builder,
137-
last_timestamp: latest_header.timestamp(),
138-
last_block_hashes: VecDeque::from([latest_header.hash()]),
140+
last_block_hashes: VecDeque::from([last_header.hash()]),
141+
last_header,
139142
}
140143
}
141144

@@ -193,19 +196,11 @@ where
193196
/// Generates payload attributes for a new block, passes them to FCU and inserts built payload
194197
/// through newPayload.
195198
async fn advance(&mut self) -> eyre::Result<()> {
196-
let timestamp = std::cmp::max(
197-
self.last_timestamp.saturating_add(1),
198-
std::time::SystemTime::now()
199-
.duration_since(UNIX_EPOCH)
200-
.expect("cannot be earlier than UNIX_EPOCH")
201-
.as_secs(),
202-
);
203-
204199
let res = self
205200
.to_engine
206201
.fork_choice_updated(
207202
self.forkchoice_state(),
208-
Some(self.payload_attributes_builder.build(timestamp)),
203+
Some(self.payload_attributes_builder.build(&self.last_header)),
209204
EngineApiMessageVersion::default(),
210205
)
211206
.await?;
@@ -222,17 +217,16 @@ where
222217
eyre::bail!("No payload")
223218
};
224219

225-
let block = payload.block();
226-
220+
let header = payload.block().sealed_header().clone();
227221
let payload = T::block_to_payload(payload.block().clone());
228222
let res = self.to_engine.new_payload(payload).await?;
229223

230224
if !res.is_valid() {
231225
eyre::bail!("Invalid payload")
232226
}
233227

234-
self.last_timestamp = timestamp;
235-
self.last_block_hashes.push_back(block.hash());
228+
self.last_block_hashes.push_back(header.hash());
229+
self.last_header = header;
236230
// ensure we keep at most 64 blocks
237231
if self.last_block_hashes.len() > 64 {
238232
self.last_block_hashes.pop_front();

crates/engine/local/src/payload.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
//! The implementation of the [`PayloadAttributesBuilder`] for the
22
//! [`LocalMiner`](super::LocalMiner).
33
4+
use alloy_consensus::BlockHeader;
45
use alloy_primitives::{Address, B256};
5-
use reth_chainspec::EthereumHardforks;
6+
use reth_chainspec::{EthChainSpec, EthereumHardforks};
67
use reth_ethereum_engine_primitives::EthPayloadAttributes;
78
use reth_payload_primitives::PayloadAttributesBuilder;
9+
use reth_primitives_traits::SealedHeader;
810
use std::sync::Arc;
911

1012
/// The attributes builder for local Ethereum payload.
@@ -13,21 +15,36 @@ use std::sync::Arc;
1315
pub struct LocalPayloadAttributesBuilder<ChainSpec> {
1416
/// The chainspec
1517
pub chain_spec: Arc<ChainSpec>,
18+
19+
/// Whether to enforce increasing timestamp.
20+
pub enforce_increasing_timestamp: bool,
1621
}
1722

1823
impl<ChainSpec> LocalPayloadAttributesBuilder<ChainSpec> {
1924
/// Creates a new instance of the builder.
2025
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
21-
Self { chain_spec }
26+
Self { chain_spec, enforce_increasing_timestamp: true }
27+
}
28+
29+
/// Creates a new instance of the builder without enforcing increasing timestamps.
30+
pub fn without_increasing_timestamp(self) -> Self {
31+
Self { enforce_increasing_timestamp: false, ..self }
2232
}
2333
}
2434

25-
impl<ChainSpec> PayloadAttributesBuilder<EthPayloadAttributes>
35+
impl<ChainSpec> PayloadAttributesBuilder<EthPayloadAttributes, ChainSpec::Header>
2636
for LocalPayloadAttributesBuilder<ChainSpec>
2737
where
28-
ChainSpec: Send + Sync + EthereumHardforks + 'static,
38+
ChainSpec: EthChainSpec + EthereumHardforks + 'static,
2939
{
30-
fn build(&self, timestamp: u64) -> EthPayloadAttributes {
40+
fn build(&self, parent: &SealedHeader<ChainSpec::Header>) -> EthPayloadAttributes {
41+
let mut timestamp =
42+
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
43+
44+
if self.enforce_increasing_timestamp {
45+
timestamp = std::cmp::max(parent.timestamp().saturating_add(1), timestamp);
46+
}
47+
3148
EthPayloadAttributes {
3249
timestamp,
3350
prev_randao: B256::random(),
@@ -45,14 +62,18 @@ where
4562
}
4663

4764
#[cfg(feature = "op")]
48-
impl<ChainSpec> PayloadAttributesBuilder<op_alloy_rpc_types_engine::OpPayloadAttributes>
65+
impl<ChainSpec>
66+
PayloadAttributesBuilder<op_alloy_rpc_types_engine::OpPayloadAttributes, ChainSpec::Header>
4967
for LocalPayloadAttributesBuilder<ChainSpec>
5068
where
51-
ChainSpec: Send + Sync + EthereumHardforks + 'static,
69+
ChainSpec: EthChainSpec + EthereumHardforks + 'static,
5270
{
53-
fn build(&self, timestamp: u64) -> op_alloy_rpc_types_engine::OpPayloadAttributes {
71+
fn build(
72+
&self,
73+
parent: &SealedHeader<ChainSpec::Header>,
74+
) -> op_alloy_rpc_types_engine::OpPayloadAttributes {
5475
op_alloy_rpc_types_engine::OpPayloadAttributes {
55-
payload_attributes: self.build(timestamp),
76+
payload_attributes: self.build(parent),
5677
// Add dummy system transaction
5778
transactions: Some(vec![
5879
reth_optimism_chainspec::constants::TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056

crates/node/builder/src/launch/debug.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use reth_chainspec::EthChainSpec;
77
use reth_consensus_debug_client::{DebugConsensusClient, EtherscanBlockProvider, RpcBlockProvider};
88
use reth_engine_local::LocalMiner;
99
use reth_node_api::{
10-
BlockTy, FullNodeComponents, PayloadAttrTy, PayloadAttributesBuilder, PayloadTypes,
10+
BlockTy, FullNodeComponents, HeaderTy, PayloadAttrTy, PayloadAttributesBuilder, PayloadTypes,
1111
};
1212
use std::{
1313
future::{Future, IntoFuture},
@@ -73,9 +73,7 @@ pub trait DebugNode<N: FullNodeComponents>: Node<N> {
7373
/// be constructed during local mining.
7474
fn local_payload_attributes_builder(
7575
chain_spec: &Self::ChainSpec,
76-
) -> impl PayloadAttributesBuilder<
77-
<<Self as reth_node_api::NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
78-
>;
76+
) -> impl PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes, HeaderTy<Self>>;
7977
}
8078

8179
/// Node launcher with support for launching various debugging utilities.
@@ -120,7 +118,7 @@ where
120118
inner: L,
121119
target: Target,
122120
local_payload_attributes_builder:
123-
Option<Box<dyn PayloadAttributesBuilder<PayloadAttrTy<N::Types>>>>,
121+
Option<Box<dyn PayloadAttributesBuilder<PayloadAttrTy<N::Types>, HeaderTy<N::Types>>>>,
124122
map_attributes:
125123
Option<Box<dyn Fn(PayloadAttrTy<N::Types>) -> PayloadAttrTy<N::Types> + Send + Sync>>,
126124
}
@@ -133,7 +131,7 @@ where
133131
{
134132
pub fn with_payload_attributes_builder(
135133
self,
136-
builder: impl PayloadAttributesBuilder<PayloadAttrTy<N::Types>>,
134+
builder: impl PayloadAttributesBuilder<PayloadAttrTy<N::Types>, HeaderTy<N::Types>>,
137135
) -> Self {
138136
Self {
139137
inner: self.inner,
@@ -229,7 +227,7 @@ where
229227
} else {
230228
let local = N::Types::local_payload_attributes_builder(&chain_spec);
231229
let builder = if let Some(f) = map_attributes {
232-
Either::Left(move |block_number| f(local.build(block_number)))
230+
Either::Left(move |parent| f(local.build(&parent)))
233231
} else {
234232
Either::Right(local)
235233
};

crates/payload/primitives/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ reth-execution-types.workspace = true
2121
reth-trie-common.workspace = true
2222

2323
# alloy
24+
alloy-consensus.workspace = true
2425
alloy-eips.workspace = true
2526
alloy-primitives.workspace = true
2627
alloy-rpc-types-engine = { workspace = true, features = ["serde"] }
@@ -50,6 +51,7 @@ std = [
5051
"thiserror/std",
5152
"reth-primitives-traits/std",
5253
"either/std",
54+
"alloy-consensus/std",
5355
]
5456
op = [
5557
"dep:op-alloy-rpc-types-engine",

crates/payload/primitives/src/traits.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -197,40 +197,44 @@ impl PayloadAttributes for op_alloy_rpc_types_engine::OpPayloadAttributes {
197197
///
198198
/// Enables different strategies for generating payload attributes based on
199199
/// contextual information. Useful for testing and specialized building.
200-
pub trait PayloadAttributesBuilder<Attributes>: Send + Sync + 'static {
200+
pub trait PayloadAttributesBuilder<Attributes, Header = alloy_consensus::Header>:
201+
Send + Sync + 'static
202+
{
201203
/// Constructs new payload attributes for the given timestamp.
202-
fn build(&self, timestamp: u64) -> Attributes;
204+
fn build(&self, parent: &SealedHeader<Header>) -> Attributes;
203205
}
204206

205-
impl<Attributes, F> PayloadAttributesBuilder<Attributes> for F
207+
impl<Attributes, Header, F> PayloadAttributesBuilder<Attributes, Header> for F
206208
where
207-
F: Fn(u64) -> Attributes + Send + Sync + 'static,
209+
Header: Clone,
210+
F: Fn(SealedHeader<Header>) -> Attributes + Send + Sync + 'static,
208211
{
209-
fn build(&self, timestamp: u64) -> Attributes {
210-
self(timestamp)
212+
fn build(&self, parent: &SealedHeader<Header>) -> Attributes {
213+
self(parent.clone())
211214
}
212215
}
213216

214-
impl<Attributes, L, R> PayloadAttributesBuilder<Attributes> for Either<L, R>
217+
impl<Attributes, Header, L, R> PayloadAttributesBuilder<Attributes, Header> for Either<L, R>
215218
where
216-
L: PayloadAttributesBuilder<Attributes>,
217-
R: PayloadAttributesBuilder<Attributes>,
219+
L: PayloadAttributesBuilder<Attributes, Header>,
220+
R: PayloadAttributesBuilder<Attributes, Header>,
218221
{
219-
fn build(&self, timestamp: u64) -> Attributes {
222+
fn build(&self, parent: &SealedHeader<Header>) -> Attributes {
220223
match self {
221-
Self::Left(l) => l.build(timestamp),
222-
Self::Right(r) => r.build(timestamp),
224+
Self::Left(l) => l.build(parent),
225+
Self::Right(r) => r.build(parent),
223226
}
224227
}
225228
}
226229

227-
impl<Attributes> PayloadAttributesBuilder<Attributes>
228-
for Box<dyn PayloadAttributesBuilder<Attributes>>
230+
impl<Attributes, Header> PayloadAttributesBuilder<Attributes, Header>
231+
for Box<dyn PayloadAttributesBuilder<Attributes, Header>>
229232
where
233+
Header: 'static,
230234
Attributes: 'static,
231235
{
232-
fn build(&self, timestamp: u64) -> Attributes {
233-
self.as_ref().build(timestamp)
236+
fn build(&self, parent: &SealedHeader<Header>) -> Attributes {
237+
self.as_ref().build(parent)
234238
}
235239
}
236240

0 commit comments

Comments
 (0)