diff --git a/Cargo.lock b/Cargo.lock index 9919f959a..f41fa41fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5779,6 +5779,7 @@ dependencies = [ "reth-payload-builder", "reth-primitives", "reth-provider", + "reth-rlp", "reth-rpc-api", "reth-rpc-types", "reth-tasks", @@ -5791,10 +5792,8 @@ dependencies = [ name = "reth-rpc-types" version = "0.1.0-alpha.4" dependencies = [ - "assert_matches", "jsonrpsee-types", "rand 0.8.5", - "reth-interfaces", "reth-primitives", "reth-rlp", "serde", diff --git a/crates/rpc/rpc-engine-api/Cargo.toml b/crates/rpc/rpc-engine-api/Cargo.toml index fc09c6001..aa5157add 100644 --- a/crates/rpc/rpc-engine-api/Cargo.toml +++ b/crates/rpc/rpc-engine-api/Cargo.toml @@ -30,6 +30,7 @@ jsonrpsee-core = "0.18" tracing.workspace = true [dev-dependencies] +reth-rlp.workspace = true reth-interfaces = { workspace = true, features = ["test-utils"] } reth-provider = { workspace = true, features = ["test-utils"] } reth-payload-builder = { workspace = true, features = ["test-utils"] } diff --git a/crates/rpc/rpc-engine-api/src/lib.rs b/crates/rpc/rpc-engine-api/src/lib.rs index fa440801b..ba36670c5 100644 --- a/crates/rpc/rpc-engine-api/src/lib.rs +++ b/crates/rpc/rpc-engine-api/src/lib.rs @@ -29,3 +29,10 @@ pub use message::EngineApiMessageVersion; // re-export server trait for convenience pub use reth_rpc_api::EngineApiServer; + +#[cfg(test)] +#[allow(unused_imports)] +mod tests { + // silence unused import warning + use reth_rlp as _; +} diff --git a/crates/rpc/rpc-engine-api/tests/it/main.rs b/crates/rpc/rpc-engine-api/tests/it/main.rs new file mode 100644 index 000000000..cf02e7098 --- /dev/null +++ b/crates/rpc/rpc-engine-api/tests/it/main.rs @@ -0,0 +1,3 @@ +mod payload; + +fn main() {} diff --git a/crates/rpc/rpc-engine-api/tests/it/payload.rs b/crates/rpc/rpc-engine-api/tests/it/payload.rs new file mode 100644 index 000000000..bc9f1f424 --- /dev/null +++ b/crates/rpc/rpc-engine-api/tests/it/payload.rs @@ -0,0 +1,127 @@ +//! Some payload tests + +use assert_matches::assert_matches; +use reth_interfaces::test_utils::generators::{ + self, random_block, random_block_range, random_header, +}; +use reth_primitives::{ + bytes::{Bytes, BytesMut}, + proofs::{self}, + Block, SealedBlock, TransactionSigned, H256, U256, +}; +use reth_rlp::{Decodable, DecodeError}; +use reth_rpc_types::engine::{ExecutionPayload, ExecutionPayloadBodyV1, PayloadError}; + +fn transform_block Block>(src: SealedBlock, f: F) -> ExecutionPayload { + let unsealed = src.unseal(); + let mut transformed: Block = f(unsealed); + // Recalculate roots + transformed.header.transactions_root = proofs::calculate_transaction_root(&transformed.body); + transformed.header.ommers_hash = proofs::calculate_ommers_root(&transformed.ommers); + SealedBlock { + header: transformed.header.seal_slow(), + body: transformed.body, + ommers: transformed.ommers, + withdrawals: transformed.withdrawals, + } + .into() +} + +#[test] +fn payload_body_roundtrip() { + let mut rng = generators::rng(); + for block in random_block_range(&mut rng, 0..=99, H256::default(), 0..2) { + let unsealed = block.clone().unseal(); + let payload_body: ExecutionPayloadBodyV1 = unsealed.into(); + + assert_eq!( + Ok(block.body), + payload_body + .transactions + .iter() + .map(|x| TransactionSigned::decode(&mut &x[..])) + .collect::, _>>(), + ); + + assert_eq!(block.withdrawals, payload_body.withdrawals); + } +} + +#[test] +fn payload_validation() { + let mut rng = generators::rng(); + let block = random_block(&mut rng, 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!(TryInto::::try_into(block_with_valid_extra_data), Ok(_)); + + // Invalid extra data + let block_with_invalid_extra_data: Bytes = BytesMut::zeroed(33).freeze(); + let invalid_extra_data_block = transform_block(block.clone(), |mut b| { + b.header.extra_data = block_with_invalid_extra_data.clone().into(); + b + }); + assert_matches!( + TryInto::::try_into(invalid_extra_data_block), + Err(PayloadError::ExtraData(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!( + TryInto::::try_into(block_with_zero_base_fee), + Err(PayloadError::BaseFee(val)) if val == U256::ZERO + ); + + // 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!( + TryInto::::try_into(payload_with_invalid_txs), + Err(PayloadError::Decode(DecodeError::InputTooShort)) + ); + + // Non empty ommers + let block_with_ommers = transform_block(block.clone(), |mut b| { + b.ommers.push(random_header(&mut rng, 100, None).unseal()); + b + }); + assert_matches!( + TryInto::::try_into(block_with_ommers.clone()), + Err(PayloadError::BlockHash { consensus, .. }) + if consensus == block_with_ommers.block_hash + ); + + // None zero difficulty + let block_with_difficulty = transform_block(block.clone(), |mut b| { + b.header.difficulty = U256::from(1); + b + }); + assert_matches!( + TryInto::::try_into(block_with_difficulty.clone()), + Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash + ); + + // None zero nonce + let block_with_nonce = transform_block(block.clone(), |mut b| { + b.header.nonce = 1; + b + }); + assert_matches!( + TryInto::::try_into(block_with_nonce.clone()), + Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash + ); + + // Valid block + let valid_block = block; + assert_matches!(TryInto::::try_into(valid_block), Ok(_)); +} diff --git a/crates/rpc/rpc-types/Cargo.toml b/crates/rpc/rpc-types/Cargo.toml index 8ca59c36c..71c1b80be 100644 --- a/crates/rpc/rpc-types/Cargo.toml +++ b/crates/rpc/rpc-types/Cargo.toml @@ -24,10 +24,6 @@ serde_json.workspace = true jsonrpsee-types = { version = "0.18" } [dev-dependencies] -# reth -reth-interfaces = { workspace = true, features = ["test-utils"] } - # misc rand.workspace = true -assert_matches = "1.5" similar-asserts = "1.4" diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index f187ec662..037d5739e 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -446,130 +446,6 @@ pub enum PayloadValidationError { #[cfg(test)] mod tests { use super::*; - use assert_matches::assert_matches; - use reth_interfaces::test_utils::generators::{ - self, random_block, random_block_range, random_header, - }; - use reth_primitives::{ - bytes::{Bytes, BytesMut}, - TransactionSigned, H256, - }; - use reth_rlp::{Decodable, DecodeError}; - - fn transform_block Block>(src: SealedBlock, f: F) -> ExecutionPayload { - let unsealed = src.unseal(); - let mut transformed: Block = f(unsealed); - // Recalculate roots - transformed.header.transactions_root = - proofs::calculate_transaction_root(&transformed.body); - transformed.header.ommers_hash = proofs::calculate_ommers_root(&transformed.ommers); - SealedBlock { - header: transformed.header.seal_slow(), - body: transformed.body, - ommers: transformed.ommers, - withdrawals: transformed.withdrawals, - } - .into() - } - - #[test] - fn payload_body_roundtrip() { - let mut rng = generators::rng(); - for block in random_block_range(&mut rng, 0..=99, H256::default(), 0..2) { - let unsealed = block.clone().unseal(); - let payload_body: ExecutionPayloadBodyV1 = unsealed.into(); - - assert_eq!( - Ok(block.body), - payload_body - .transactions - .iter() - .map(|x| TransactionSigned::decode(&mut &x[..])) - .collect::, _>>(), - ); - - assert_eq!(block.withdrawals, payload_body.withdrawals); - } - } - - #[test] - fn payload_validation() { - let mut rng = generators::rng(); - let block = random_block(&mut rng, 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!(TryInto::::try_into(block_with_valid_extra_data), Ok(_)); - - // Invalid extra data - let block_with_invalid_extra_data: Bytes = BytesMut::zeroed(33).freeze(); - let invalid_extra_data_block = transform_block(block.clone(), |mut b| { - b.header.extra_data = block_with_invalid_extra_data.clone().into(); - b - }); - assert_matches!( - TryInto::::try_into(invalid_extra_data_block), - Err(PayloadError::ExtraData(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!( - TryInto::::try_into(block_with_zero_base_fee), - Err(PayloadError::BaseFee(val)) if val == U256::ZERO - ); - - // 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!( - TryInto::::try_into(payload_with_invalid_txs), - Err(PayloadError::Decode(DecodeError::InputTooShort)) - ); - - // Non empty ommers - let block_with_ommers = transform_block(block.clone(), |mut b| { - b.ommers.push(random_header(&mut rng, 100, None).unseal()); - b - }); - assert_matches!( - TryInto::::try_into(block_with_ommers.clone()), - Err(PayloadError::BlockHash { consensus, .. }) - if consensus == block_with_ommers.block_hash - ); - - // None zero difficulty - let block_with_difficulty = transform_block(block.clone(), |mut b| { - b.header.difficulty = U256::from(1); - b - }); - assert_matches!( - TryInto::::try_into(block_with_difficulty.clone()), - Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash - ); - - // None zero nonce - let block_with_nonce = transform_block(block.clone(), |mut b| { - b.header.nonce = 1; - b - }); - assert_matches!( - TryInto::::try_into(block_with_nonce.clone()), - Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash - ); - - // Valid block - let valid_block = block; - assert_matches!(TryInto::::try_into(valid_block), Ok(_)); - } #[test] fn serde_payload_status() {