mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-02-10 06:55:10 -05:00
test(engine): consensus engine tests (#648)
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -3457,8 +3457,10 @@ dependencies = [
|
||||
name = "reth-consensus"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"auto_impl",
|
||||
"bytes",
|
||||
"futures",
|
||||
"reth-executor",
|
||||
"reth-interfaces",
|
||||
@@ -3911,6 +3913,7 @@ dependencies = [
|
||||
name = "reth-rpc-types"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"reth-primitives",
|
||||
"reth-rlp",
|
||||
"serde",
|
||||
|
||||
@@ -26,5 +26,11 @@ thiserror = "1.0.37"
|
||||
auto_impl = "1.0"
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-interfaces = { path = "../interfaces", features = ["test-utils"] }
|
||||
reth-provider = { path = "../storage/provider", features = ["test-utils"] }
|
||||
assert_matches = "1.5.0"
|
||||
bytes = "1.2"
|
||||
|
||||
[features]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
@@ -189,18 +189,22 @@ impl<Client: HeaderProvider + BlockProvider + StateProvider> ConsensusEngine
|
||||
return Ok(PayloadStatus::from_status(PayloadStatusEnum::Syncing))
|
||||
};
|
||||
|
||||
let parent_td = self.client.header_td(&block.parent_hash)?;
|
||||
if parent_td.unwrap_or_default() <= self.config.merge_terminal_total_difficulty.into() {
|
||||
return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: EngineApiError::PayloadPreMerge.to_string(),
|
||||
}))
|
||||
if let Some(parent_td) = self.client.header_td(&block.parent_hash)? {
|
||||
if parent_td <= self.config.merge_terminal_total_difficulty.into() {
|
||||
return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: EngineApiError::PayloadPreMerge.to_string(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
if block.timestamp <= parent.timestamp {
|
||||
return Err(EngineApiError::PayloadTimestamp {
|
||||
invalid: block.timestamp,
|
||||
latest: parent.timestamp,
|
||||
})
|
||||
return Ok(PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: EngineApiError::PayloadTimestamp {
|
||||
invalid: block.timestamp,
|
||||
latest: parent.timestamp,
|
||||
}
|
||||
.to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
let (header, body, _) = block.split();
|
||||
@@ -322,3 +326,420 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
use reth_interfaces::test_utils::generators::random_block;
|
||||
use reth_primitives::H256;
|
||||
use reth_provider::test_utils::MockEthProvider;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
|
||||
mod new_payload {
|
||||
use super::*;
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use reth_interfaces::test_utils::generators::random_header;
|
||||
use reth_primitives::Block;
|
||||
use reth_rlp::DecodeError;
|
||||
|
||||
fn transform_block<F: FnOnce(Block) -> Block>(src: SealedBlock, f: F) -> SealedBlock {
|
||||
let unsealed = src.unseal();
|
||||
let mut transformed: Block = f(unsealed);
|
||||
// Recalculate roots
|
||||
transformed.header.transactions_root =
|
||||
proofs::calculate_transaction_root(transformed.body.iter());
|
||||
transformed.header.ommers_hash =
|
||||
proofs::calculate_ommers_root(transformed.ommers.iter());
|
||||
SealedBlock {
|
||||
header: transformed.header.seal(),
|
||||
body: transformed.body,
|
||||
ommers: transformed.ommers.into_iter().map(Header::seal).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn payload_validation() {
|
||||
let (_tx, rx) = unbounded_channel();
|
||||
let engine = EthConsensusEngine {
|
||||
client: Arc::new(MockEthProvider::default()),
|
||||
config: Config::default(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
let block = random_block(100, Some(H256::random()), Some(3), Some(0));
|
||||
|
||||
// Valid extra data
|
||||
let block_with_valid_extra_data = transform_block(block.clone(), |mut b| {
|
||||
b.header.extra_data = BytesMut::zeroed(32).freeze().into();
|
||||
b
|
||||
});
|
||||
assert_matches!(engine.try_construct_block(block_with_valid_extra_data.into()), Ok(_));
|
||||
|
||||
// Invalid extra data
|
||||
let block_with_invalid_extra_data: Bytes = BytesMut::zeroed(33).freeze().into();
|
||||
let invalid_extra_data_block = transform_block(block.clone(), |mut b| {
|
||||
b.header.extra_data = block_with_invalid_extra_data.clone();
|
||||
b
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(invalid_extra_data_block.into()),
|
||||
Err(EngineApiError::PayloadExtraData(data)) if data == block_with_invalid_extra_data
|
||||
);
|
||||
|
||||
// Zero base fee
|
||||
let block_with_zero_base_fee = transform_block(block.clone(), |mut b| {
|
||||
b.header.base_fee_per_gas = Some(0);
|
||||
b
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(block_with_zero_base_fee.into()),
|
||||
Err(EngineApiError::PayloadBaseFee(val)) if val == 0.into()
|
||||
);
|
||||
|
||||
// Invalid encoded transactions
|
||||
let mut payload_with_invalid_txs: ExecutionPayload = block.clone().into();
|
||||
payload_with_invalid_txs.transactions.iter_mut().for_each(|tx| {
|
||||
*tx = Bytes::new().into();
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(payload_with_invalid_txs),
|
||||
Err(EngineApiError::Decode(DecodeError::InputTooShort))
|
||||
);
|
||||
|
||||
// Non empty ommers
|
||||
let block_with_ommers = transform_block(block.clone(), |mut b| {
|
||||
b.ommers.push(random_header(100, None).unseal());
|
||||
b
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(block_with_ommers.clone().into()),
|
||||
Err(EngineApiError::PayloadBlockHash { consensus, .. })
|
||||
if consensus == block_with_ommers.hash()
|
||||
);
|
||||
|
||||
// None zero difficulty
|
||||
let block_with_difficulty = transform_block(block.clone(), |mut b| {
|
||||
b.header.difficulty = 1.into();
|
||||
b
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(block_with_difficulty.clone().into()),
|
||||
Err(EngineApiError::PayloadBlockHash { consensus, .. })
|
||||
if consensus == block_with_difficulty.hash()
|
||||
);
|
||||
|
||||
// None zero nonce
|
||||
let block_with_nonce = transform_block(block.clone(), |mut b| {
|
||||
b.header.nonce = 1;
|
||||
b
|
||||
});
|
||||
assert_matches!(
|
||||
engine.try_construct_block(block_with_nonce.clone().into()),
|
||||
Err(EngineApiError::PayloadBlockHash { consensus, .. })
|
||||
if consensus == block_with_nonce.hash()
|
||||
);
|
||||
|
||||
// Valid block
|
||||
let valid_block = block.clone();
|
||||
assert_matches!(engine.try_construct_block(valid_block.into()), Ok(_));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn payload_known() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let client = Arc::new(MockEthProvider::default());
|
||||
let engine = EthConsensusEngine {
|
||||
client: client.clone(),
|
||||
config: Config::default(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let block = random_block(100, Some(H256::random()), None, Some(0)); // payload must have no ommers
|
||||
let block_hash = block.hash();
|
||||
let execution_payload = block.clone().into();
|
||||
|
||||
client.add_header(block_hash, block.header.unseal());
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::NewPayload(execution_payload, result_tx))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
let result = result_rx.await;
|
||||
assert_matches!(result, Ok(Ok(_)));
|
||||
let expected_result = PayloadStatus::new(PayloadStatusEnum::Valid, block_hash);
|
||||
assert_eq!(result.unwrap().unwrap(), expected_result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn payload_parent_unknown() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let engine = EthConsensusEngine {
|
||||
client: Arc::new(MockEthProvider::default()),
|
||||
config: Config::default(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
let block = random_block(100, Some(H256::random()), None, Some(0)); // payload must have no ommers
|
||||
tx.send(EngineMessage::NewPayload(block.into(), result_tx))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
let result = result_rx.await;
|
||||
assert_matches!(result, Ok(Ok(_)));
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Syncing);
|
||||
assert_eq!(result.unwrap().unwrap(), expected_result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn payload_pre_merge() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let config = Config::default();
|
||||
let client = Arc::new(MockEthProvider::default());
|
||||
let engine = EthConsensusEngine {
|
||||
client: client.clone(),
|
||||
config: config.clone(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
let parent = transform_block(random_block(100, None, None, Some(0)), |mut b| {
|
||||
b.header.difficulty = config.merge_terminal_total_difficulty.into();
|
||||
b
|
||||
});
|
||||
let block = random_block(101, Some(parent.hash()), None, Some(0));
|
||||
|
||||
client.add_block(parent.hash(), parent.clone().unseal());
|
||||
|
||||
tx.send(EngineMessage::NewPayload(block.clone().into(), result_tx))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
let result = result_rx.await;
|
||||
assert_matches!(result, Ok(Ok(_)));
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: EngineApiError::PayloadPreMerge.to_string(),
|
||||
});
|
||||
assert_eq!(result.unwrap().unwrap(), expected_result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalid_payload_timestamp() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let config = Config::default();
|
||||
let client = Arc::new(MockEthProvider::default());
|
||||
let engine = EthConsensusEngine {
|
||||
client: client.clone(),
|
||||
config: config.clone(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
let block_timestamp = 100;
|
||||
let parent_timestamp = block_timestamp + 10;
|
||||
let parent = transform_block(random_block(100, None, None, Some(0)), |mut b| {
|
||||
b.header.timestamp = parent_timestamp;
|
||||
b.header.difficulty = (config.merge_terminal_total_difficulty + 1).into();
|
||||
b
|
||||
});
|
||||
let block =
|
||||
transform_block(random_block(101, Some(parent.hash()), None, Some(0)), |mut b| {
|
||||
b.header.timestamp = block_timestamp;
|
||||
b
|
||||
});
|
||||
|
||||
client.add_block(parent.hash(), parent.clone().unseal());
|
||||
|
||||
tx.send(EngineMessage::NewPayload(block.clone().into(), result_tx))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
let result = result_rx.await;
|
||||
assert_matches!(result, Ok(Ok(_)));
|
||||
let expected_result = PayloadStatus::from_status(PayloadStatusEnum::Invalid {
|
||||
validation_error: EngineApiError::PayloadTimestamp {
|
||||
invalid: block_timestamp,
|
||||
latest: parent_timestamp,
|
||||
}
|
||||
.to_string(),
|
||||
});
|
||||
assert_eq!(result.unwrap().unwrap(), expected_result);
|
||||
}
|
||||
|
||||
// TODO: add execution tests
|
||||
}
|
||||
|
||||
// non exhaustive tests for engine_getPayload
|
||||
// TODO: amend when block building is implemented
|
||||
mod get_payload {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn payload_unknown() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let engine = EthConsensusEngine {
|
||||
client: Arc::new(MockEthProvider::default()),
|
||||
config: Config::default(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let payload_id = H64::random();
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::GetPayload(payload_id, result_tx))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
assert_matches!(result_rx.await, Ok(Err(EngineApiError::PayloadUnknown)));
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#specification-3
|
||||
mod exchange_transition_configuration {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn terminal_td_mismatch() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let config = Config::default();
|
||||
let engine = EthConsensusEngine {
|
||||
client: Arc::new(MockEthProvider::default()),
|
||||
config: config.clone(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let transition_config = TransitionConfiguration {
|
||||
terminal_total_difficulty: (config.merge_terminal_total_difficulty + 1).into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::ExchangeTransitionConfiguration(
|
||||
transition_config.clone(),
|
||||
result_tx,
|
||||
))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
assert_matches!(
|
||||
result_rx.await,
|
||||
Ok(Err(EngineApiError::TerminalTD { execution, consensus }))
|
||||
if execution == config.merge_terminal_total_difficulty.into()
|
||||
&& consensus == transition_config.terminal_total_difficulty.into()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn terminal_block_hash_mismatch() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let client = Arc::new(MockEthProvider::default());
|
||||
let config = Config::default();
|
||||
let engine = EthConsensusEngine {
|
||||
client: client.clone(),
|
||||
config: config.clone(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let terminal_block_number = 1000;
|
||||
let consensus_terminal_block = random_block(terminal_block_number, None, None, None);
|
||||
let execution_terminal_block = random_block(terminal_block_number, None, None, None);
|
||||
|
||||
let transition_config = TransitionConfiguration {
|
||||
terminal_total_difficulty: config.merge_terminal_total_difficulty.into(),
|
||||
terminal_block_hash: consensus_terminal_block.hash(),
|
||||
terminal_block_number,
|
||||
};
|
||||
|
||||
// Unknown block number
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::ExchangeTransitionConfiguration(
|
||||
transition_config.clone(),
|
||||
result_tx,
|
||||
))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
assert_matches!(
|
||||
result_rx.await,
|
||||
Ok(Err(EngineApiError::TerminalBlockHash { execution, consensus }))
|
||||
if execution.is_none()
|
||||
&& consensus == transition_config.terminal_block_hash
|
||||
);
|
||||
|
||||
// Add block and to provider local store and test for mismatch
|
||||
client.add_block(
|
||||
execution_terminal_block.hash(),
|
||||
execution_terminal_block.clone().unseal(),
|
||||
);
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::ExchangeTransitionConfiguration(
|
||||
transition_config.clone(),
|
||||
result_tx,
|
||||
))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
assert_matches!(
|
||||
result_rx.await,
|
||||
Ok(Err(EngineApiError::TerminalBlockHash { execution, consensus }))
|
||||
if execution == Some(execution_terminal_block.hash())
|
||||
&& consensus == transition_config.terminal_block_hash
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn configurations_match() {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
let client = Arc::new(MockEthProvider::default());
|
||||
let config = Config::default();
|
||||
let engine = EthConsensusEngine {
|
||||
client: client.clone(),
|
||||
config: config.clone(),
|
||||
local_store: Default::default(),
|
||||
rx: UnboundedReceiverStream::new(rx),
|
||||
};
|
||||
|
||||
tokio::spawn(engine);
|
||||
|
||||
let terminal_block_number = 1000;
|
||||
let terminal_block = random_block(terminal_block_number, None, None, None);
|
||||
|
||||
let transition_config = TransitionConfiguration {
|
||||
terminal_total_difficulty: config.merge_terminal_total_difficulty.into(),
|
||||
terminal_block_hash: terminal_block.hash(),
|
||||
terminal_block_number,
|
||||
};
|
||||
|
||||
client.add_block(terminal_block.hash(), terminal_block.clone().unseal());
|
||||
|
||||
let (result_tx, result_rx) = oneshot::channel();
|
||||
tx.send(EngineMessage::ExchangeTransitionConfiguration(
|
||||
transition_config.clone(),
|
||||
result_tx,
|
||||
))
|
||||
.expect("failed to send engine msg");
|
||||
|
||||
assert_matches!(
|
||||
result_rx.await,
|
||||
Ok(Ok(config)) if config == transition_config
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,20 +100,23 @@ pub fn sign_message(secret: H256, message: H256) -> Result<Signature, secp256k1:
|
||||
/// transactions in the block.
|
||||
///
|
||||
/// The ommer headers are not assumed to be valid.
|
||||
pub fn random_block(number: u64, parent: Option<H256>, tx_count: Option<u8>) -> SealedBlock {
|
||||
pub fn random_block(
|
||||
number: u64,
|
||||
parent: Option<H256>,
|
||||
tx_count: Option<u8>,
|
||||
ommers_count: Option<u8>,
|
||||
) -> SealedBlock {
|
||||
let mut rng = thread_rng();
|
||||
|
||||
// Generate transactions
|
||||
let tx_count = tx_count.unwrap_or(rand::random::<u8>());
|
||||
let transactions: Vec<TransactionSigned> =
|
||||
(0..tx_count).into_iter().map(|_| random_signed_tx()).collect();
|
||||
let tx_count = tx_count.unwrap_or(rng.gen::<u8>());
|
||||
let transactions: Vec<TransactionSigned> = (0..tx_count).map(|_| random_signed_tx()).collect();
|
||||
let total_gas = transactions.iter().fold(0, |sum, tx| sum + tx.transaction.gas_limit());
|
||||
|
||||
// Generate ommers
|
||||
let mut ommers = Vec::new();
|
||||
for _ in 0..rng.gen_range(0..2) {
|
||||
ommers.push(random_header(number, parent).unseal());
|
||||
}
|
||||
let ommers_count = ommers_count.unwrap_or(rng.gen_range(0..2));
|
||||
let ommers =
|
||||
(0..ommers_count).map(|_| random_header(number, parent).unseal()).collect::<Vec<_>>();
|
||||
|
||||
// Calculate roots
|
||||
let transactions_root = proofs::calculate_transaction_root(transactions.iter());
|
||||
@@ -127,6 +130,7 @@ pub fn random_block(number: u64, parent: Option<H256>, tx_count: Option<u8>) ->
|
||||
gas_limit: total_gas,
|
||||
transactions_root,
|
||||
ommers_hash,
|
||||
base_fee_per_gas: Some(rng.gen()),
|
||||
..Default::default()
|
||||
}
|
||||
.seal(),
|
||||
@@ -154,6 +158,7 @@ pub fn random_block_range(
|
||||
idx,
|
||||
Some(blocks.last().map(|block: &SealedBlock| block.header.hash()).unwrap_or(head)),
|
||||
Some(tx_count.clone().sample_single(&mut rng)),
|
||||
None,
|
||||
));
|
||||
}
|
||||
blocks
|
||||
|
||||
@@ -15,4 +15,5 @@ reth-rlp = { path = "../../common/rlp" }
|
||||
|
||||
# misc
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
serde_json = "1.0"
|
||||
bytes = "1.2"
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use reth_primitives::{Address, BlockNumber, Bloom, Bytes, H256, H64, U256, U64};
|
||||
use bytes::BytesMut;
|
||||
use reth_primitives::{Address, BlockNumber, Bloom, Bytes, SealedBlock, H256, H64, U256, U64};
|
||||
use reth_rlp::Encodable;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// This structure maps on the ExecutionPayload structure of the beacon chain spec.
|
||||
@@ -31,6 +33,37 @@ pub struct ExecutionPayload {
|
||||
pub withdrawal: Option<Withdrawal>,
|
||||
}
|
||||
|
||||
impl From<SealedBlock> for ExecutionPayload {
|
||||
fn from(value: SealedBlock) -> Self {
|
||||
let transactions = value
|
||||
.body
|
||||
.iter()
|
||||
.map(|tx| {
|
||||
let mut encoded = BytesMut::new();
|
||||
tx.encode(&mut encoded);
|
||||
encoded.freeze().into()
|
||||
})
|
||||
.collect();
|
||||
ExecutionPayload {
|
||||
parent_hash: value.parent_hash,
|
||||
fee_recipient: value.beneficiary,
|
||||
state_root: value.state_root,
|
||||
receipts_root: value.receipts_root,
|
||||
logs_bloom: value.logs_bloom,
|
||||
prev_randao: value.mix_hash,
|
||||
block_number: value.number.into(),
|
||||
gas_limit: value.gas_limit.into(),
|
||||
gas_used: value.gas_used.into(),
|
||||
timestamp: value.timestamp.into(),
|
||||
extra_data: value.extra_data.clone().into(),
|
||||
base_fee_per_gas: value.base_fee_per_gas.unwrap_or_default().into(),
|
||||
block_hash: value.hash(),
|
||||
transactions,
|
||||
withdrawal: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This structure maps onto the validator withdrawal object from the beacon chain spec.
|
||||
///
|
||||
/// See also: <https://github.com/ethereum/execution-apis/blob/6709c2a795b707202e93c4f2867fa0bf2640a84f/src/engine/shanghai.md#withdrawalv1>
|
||||
|
||||
@@ -161,7 +161,7 @@ mod tests {
|
||||
(stage_progress..input.previous_stage_progress() + 1)
|
||||
.map(|number| -> Result<SealedBlock, TestRunnerError> {
|
||||
let tx_count = Some((number == stage_progress + 10) as u8);
|
||||
let block = random_block(number, None, tx_count);
|
||||
let block = random_block(number, None, tx_count, None);
|
||||
current_tx_id = runner.insert_block(current_tx_id, &block, false)?;
|
||||
Ok(block)
|
||||
})
|
||||
|
||||
@@ -52,12 +52,14 @@ impl ExtendedAccount {
|
||||
impl MockEthProvider {
|
||||
/// Add block to local block store
|
||||
pub fn add_block(&self, hash: H256, block: Block) {
|
||||
self.add_header(hash, block.header.clone());
|
||||
self.blocks.lock().insert(hash, block);
|
||||
}
|
||||
|
||||
/// Add multiple blocks to local block store
|
||||
pub fn extend_blocks(&self, iter: impl IntoIterator<Item = (H256, Block)>) {
|
||||
for (hash, block) in iter.into_iter() {
|
||||
self.add_header(hash, block.header.clone());
|
||||
self.add_block(hash, block)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user