feat: add reth test-vectors compact --write|--read (#11954)

This commit is contained in:
joshieDo
2024-10-25 03:34:12 +09:00
committed by GitHub
parent ba78e43938
commit 777417ad8a
51 changed files with 857 additions and 101 deletions

View File

@ -27,6 +27,9 @@ op-alloy-consensus = { workspace = true, optional = true }
# misc
bytes.workspace = true
modular-bitfield = { workspace = true, optional = true }
visibility = { version = "0.1.1", optional = true}
serde.workspace = true
arbitrary = { workspace = true, features = ["derive"], optional = true }
[dev-dependencies]
alloy-eips = { workspace = true, default-features = false, features = [
@ -45,7 +48,6 @@ serde_json.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
proptest.workspace = true
proptest-arbitrary-interop.workspace = true
serde.workspace = true
[features]
default = ["std", "alloy"]
@ -66,4 +68,25 @@ alloy = [
"dep:alloy-trie",
]
optimism = ["alloy", "dep:op-alloy-consensus"]
test-utils = []
test-utils = [
"std",
"alloy",
"arbitrary",
"dep:visibility",
"dep:arbitrary"
]
serde = [
"alloy-consensus?/serde",
"alloy-eips?/serde",
"alloy-primitives/serde",
"alloy-trie?/serde",
"bytes/serde",
"op-alloy-consensus?/serde"
]
arbitrary = [
"alloy-consensus?/arbitrary",
"alloy-eips?/arbitrary",
"alloy-primitives/arbitrary",
"alloy-trie?/arbitrary",
"op-alloy-consensus?/arbitrary"
]

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AccessList`]
use crate::Compact;
use alloc::vec::Vec;
use alloy_eips::eip2930::{AccessList, AccessListItem};

View File

@ -1,16 +1,21 @@
use core::ops::Deref;
//! Compact implementation for [`AlloyAuthorization`]
use crate::Compact;
use alloy_eips::eip7702::{Authorization as AlloyAuthorization, SignedAuthorization};
use alloy_primitives::{Address, U256};
use bytes::Buf;
use core::ops::Deref;
use reth_codecs_derive::add_arbitrary_tests;
/// Authorization acts as bridge which simplifies Compact implementation for AlloyAuthorization.
///
/// Notice: Make sure this struct is 1:1 with `alloy_eips::eip7702::Authorization`
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct Authorization {
chain_id: u64,

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyGenesisAccount`]
use crate::Compact;
use alloc::vec::Vec;
use alloy_genesis::GenesisAccount as AlloyGenesisAccount;
@ -22,8 +24,14 @@ pub(crate) struct GenesisAccountRef<'a> {
private_key: Option<&'a B256>,
}
/// Acts as bridge which simplifies Compact implementation for
/// `AlloyGenesisAccount`.
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct GenesisAccount {
/// The nonce of the account at genesis.
@ -39,14 +47,20 @@ pub(crate) struct GenesisAccount {
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[add_arbitrary_tests(compact)]
pub(crate) struct StorageEntries {
entries: Vec<StorageEntry>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[add_arbitrary_tests(compact)]
pub(crate) struct StorageEntry {
key: B256,

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyHeader`]
use crate::Compact;
use alloy_consensus::Header as AlloyHeader;
use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, B256, U256};
@ -10,7 +12,11 @@ use alloy_primitives::{Address, BlockNumber, Bloom, Bytes, B256, U256};
/// will automatically apply to this type.
///
/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::Header`]
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(serde::Serialize, serde::Deserialize, arbitrary::Arbitrary)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
pub(crate) struct Header {
parent_hash: B256,
@ -42,7 +48,11 @@ pub(crate) struct Header {
/// used as a field of [`Header`] for backwards compatibility.
///
/// More information: <https://github.com/paradigmxyz/reth/issues/7820> & [`reth_codecs_derive::Compact`].
#[cfg_attr(test, derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(serde::Serialize, serde::Deserialize, arbitrary::Arbitrary)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
pub(crate) struct HeaderExt {
requests_hash: Option<B256>,

View File

@ -1,13 +1,29 @@
mod access_list;
mod authorization_list;
mod genesis_account;
mod header;
mod log;
mod signature;
mod transaction;
mod trie;
mod txkind;
mod withdrawal;
//! Implements Compact for alloy types.
/// Will make it a pub mod if test-utils is enabled
macro_rules! cond_mod {
($($mod_name:ident),*) => {
$(
#[cfg(feature = "test-utils")]
pub mod $mod_name;
#[cfg(not(feature = "test-utils"))]
mod $mod_name;
)*
};
}
cond_mod!(
access_list,
authorization_list,
genesis_account,
header,
log,
signature,
transaction,
trie,
txkind,
withdrawal
);
#[cfg(test)]
mod tests {

View File

@ -1,6 +1,7 @@
use alloy_primitives::{Parity, Signature, U256};
//! Compact implementation for [`Signature`]
use crate::Compact;
use alloy_primitives::{Parity, Signature, U256};
impl Compact for Signature {
fn to_compact<B>(&self, buf: &mut B) -> usize

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyTxEip1559`]
use crate::Compact;
use alloy_consensus::TxEip1559 as AlloyTxEip1559;
use alloy_eips::eip2930::AccessList;
@ -11,8 +13,12 @@ use alloy_primitives::{Bytes, ChainId, TxKind, U256};
///
/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::TxEip1559`]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Compact, Default)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(test, crate::add_arbitrary_tests(compact))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(any(test, feature = "test-utils"), crate::add_arbitrary_tests(compact))]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
pub(crate) struct TxEip1559 {
chain_id: ChainId,
nonce: u64,

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyTxEip2930`]
use crate::Compact;
use alloy_consensus::TxEip2930 as AlloyTxEip2930;
use alloy_eips::eip2930::AccessList;
@ -13,7 +15,11 @@ use reth_codecs_derive::add_arbitrary_tests;
///
/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::TxEip2930`]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct TxEip2930 {
chain_id: ChainId,

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyTxEip4844`]
use crate::{Compact, CompactPlaceholder};
use alloc::vec::Vec;
use alloy_consensus::TxEip4844 as AlloyTxEip4844;
@ -14,7 +16,8 @@ use reth_codecs_derive::add_arbitrary_tests;
///
/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::TxEip4844`]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "test-utils"), derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct TxEip4844 {
chain_id: ChainId,
@ -25,6 +28,13 @@ pub(crate) struct TxEip4844 {
/// TODO(debt): this should be removed if we break the DB.
/// Makes sure that the Compact bitflag struct has one bit after the above field:
/// <https://github.com/paradigmxyz/reth/pull/8291#issuecomment-2117545016>
#[cfg_attr(
feature = "test-utils",
serde(
serialize_with = "serialize_placeholder",
deserialize_with = "deserialize_placeholder"
)
)]
placeholder: Option<CompactPlaceholder>,
to: Address,
value: U256,
@ -75,6 +85,54 @@ impl Compact for AlloyTxEip4844 {
}
}
#[cfg(any(test, feature = "test-utils"))]
impl<'a> arbitrary::Arbitrary<'a> for TxEip4844 {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self {
chain_id: ChainId::arbitrary(u)?,
nonce: u64::arbitrary(u)?,
gas_limit: u64::arbitrary(u)?,
max_fee_per_gas: u128::arbitrary(u)?,
max_priority_fee_per_gas: u128::arbitrary(u)?,
// Should always be Some for TxEip4844
placeholder: Some(()),
to: Address::arbitrary(u)?,
value: U256::arbitrary(u)?,
access_list: AccessList::arbitrary(u)?,
blob_versioned_hashes: Vec::<B256>::arbitrary(u)?,
max_fee_per_blob_gas: u128::arbitrary(u)?,
input: Bytes::arbitrary(u)?,
})
}
}
#[cfg(any(test, feature = "test-utils"))]
fn serialize_placeholder<S>(value: &Option<()>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
// Required otherwise `serde_json` will serialize it as null and would be `None` when decoding
// it again.
match value {
Some(()) => serializer.serialize_str("placeholder"), // Custom serialization
None => serializer.serialize_none(),
}
}
#[cfg(any(test, feature = "test-utils"))]
fn deserialize_placeholder<'de, D>(deserializer: D) -> Result<Option<()>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Deserialize;
let s: Option<String> = Option::deserialize(deserializer)?;
match s.as_deref() {
Some("placeholder") => Ok(Some(())),
None => Ok(None),
_ => Err(serde::de::Error::custom("unexpected value")),
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyTxEip7702`]
use crate::Compact;
use alloc::vec::Vec;
use alloy_consensus::TxEip7702 as AlloyTxEip7702;
@ -14,7 +16,11 @@ use reth_codecs_derive::add_arbitrary_tests;
///
/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::TxEip7702`]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct TxEip7702 {
chain_id: ChainId,

View File

@ -1,11 +1,17 @@
//! Compact implementation for [`AlloyTxLegacy`]
use crate::Compact;
use alloy_consensus::TxLegacy as AlloyTxLegacy;
use alloy_primitives::{Bytes, ChainId, TxKind, U256};
/// Legacy transaction.
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(test, crate::add_arbitrary_tests(compact))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize),
crate::add_arbitrary_tests(compact)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
pub(crate) struct TxLegacy {
/// Added as EIP-155: Simple replay attack protection
chain_id: Option<ChainId>,

View File

@ -1,10 +1,18 @@
pub(crate) mod eip1559;
pub(crate) mod eip2930;
pub(crate) mod eip4844;
pub(crate) mod eip7702;
pub(crate) mod legacy;
#[cfg(feature = "optimism")]
pub(crate) mod optimism;
//! Compact implementation for transaction types
cond_mod!(
eip1559,
eip2930,
eip4844,
eip7702,
legacy
);
#[cfg(all(feature = "test-utils", feature = "optimism"))]
pub mod optimism;
#[cfg(all(not(feature = "test-utils"), feature = "optimism"))]
mod optimism;
#[cfg(test)]
mod tests {
@ -15,9 +23,13 @@ mod tests {
// this check is to ensure we do not inadvertently add too many fields to a struct which would
// expand the flags field and break backwards compatibility
use crate::alloy::transaction::{
eip1559::TxEip1559, eip2930::TxEip2930, eip4844::TxEip4844, eip7702::TxEip7702,
legacy::TxLegacy,
use alloy_primitives::hex;
use crate::{
alloy::{header::Header, transaction::{
eip1559::TxEip1559, eip2930::TxEip2930, eip4844::TxEip4844, eip7702::TxEip7702,
legacy::TxLegacy,
}},
test_utils::test_decode,
};
#[test]
@ -34,4 +46,54 @@ mod tests {
fn test_ensure_backwards_compatibility_optimism() {
assert_eq!(crate::alloy::transaction::optimism::TxDeposit::bitflag_encoded_bytes(), 2);
}
#[test]
fn test_decode_header() {
test_decode::<Header>(&hex!(
"01000000fbbb564baeafd064b979c2ac032df5cd987098066a8c6969514dfb8ecfbf043e667fa19efcc00d1dd197c309a3cc42dec820cd627af8f7f38f3274f842406891b22624431d0ea858422db8415b1181f8d19befbd21287debaf98a94e84b3ec20be846f35abfbf743ee3eda4fdda6a6f9124d295da97e26eaa1cedd09936f0a3c560b6bc10316dba5e82abd21afcf519a985feb09a6ce7fba2e8163b10f06c99828b8049c29b993d88d1d112dca60a03ebd8ebc6d69a7e1f301ca6d67c21fe0949d67bca251edf36c96a2cf7c84d98fc60a53988ac95820f434eb35280d98c8ba4d7484e7ee8fefd63591ad4c937ccaaea23871d05c77bac754c5759b34cf9b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
));
}
#[test]
fn test_decode_eip1559() {
test_decode::<TxEip1559>(&hex!(
"88086110b81b05bc5bb59ec3e4cd44e895a9dcb2656d5003e2f64ecb2e15443898cc1cc19af19ca96fc2b4eafc4abc26e4bbd70a3ddb10b7530b65eea128f4095c97164f712c04239902c1b08acf3949d4687123cdd72d5c73df113d2dc6ed7e519f410ace5553ca805975240a208b57013532de78c5cb407423ea11921ab11b13e93ef35d4d01c9a23166c4d627987545fe4675528d0ab111b0a1dc83fba0a4e1cd5c826a94db3f"
));
}
#[test]
fn test_decode_eip2930() {
test_decode::<TxEip2930>(&hex!(
"7810833fce14e3e2921e94fd3727eb71e91551d2c1e029697a654bfab510f3963aa57074015e152065d1c807f8830079fb0aeadc251d248eaec7147e78580ed638c4e667827775e24270edd5aad475776533ece65373afa71722bfeba3c900"
));
}
#[test]
fn test_decode_eip4844() {
test_decode::<TxEip4844>(&hex!(
"88086110025c359180ea680b5007c856f9e1ad4d1be7a5019feb42133f4fc4bdf74da1b457ab787462385a28a1bf8edb401adabf3ff21ac18f695e30180348ea67246fc4dc25e88add12b7c317651a0ce08946d98dbbe5b38883aa758a0f247e23b0fe3ac1bcc43d7212c984d6ccc770d70135890c9a07d715cacb9032c90d539d0b3d209a8d600178bcfb416fd489e5d5dd56d9cfc6addae810ae70bdaee65672b871dc2b3f35ec00dbaa0d872f78cb58b3199984c608c8ba"
));
}
#[test]
fn test_decode_eip7702() {
test_decode::<TxEip7702>(&hex!(
"8808210881415c034feba383d7a6efd3f2601309b33a6d682ad47168cac0f7a5c5136a33370e5e7ca7f570d5530d7a0d18bf5eac33583fdc27b6580f61e8cbd34d6de596f925c1f353188feb2c1e9e20de82a80b57f0be425d8c5896280d4f5f66cdcfba256d0c9ac8abd833859a62ec019501b4585fa176f048de4f88b93bdefecfcaf4d8f0dd04767bc683a4569c893632e44ba9d53f90d758125c9b24c0192a649166520cd5eecbc110b53eda400cf184b8ef9932c81d0deb2ea27dfa863392a87bfd53af3ec67379f20992501e76e387cbe3933861beead1b49649383cf8b2a2d5c6d04b7edc376981ed9b12cf7199fe7fabf5198659e001bed40922969b82a6cd000000000000"
));
}
#[test]
fn test_decode_legacy() {
test_decode::<TxLegacy>(&hex!(
"112210080a8ba06a8d108540bb3140e9f71a0812c46226f9ea77ae880d98d19fe27e5911801175c3b32620b2e887af0296af343526e439b775ee3b1c06750058e9e5fc4cd5965c3010f86184"
));
}
#[cfg(feature = "optimism")]
#[test]
fn test_decode_deposit() {
test_decode::<op_alloy_consensus::TxDeposit>(&hex!(
"8108ac8f15983d59b6ae4911a00ff7bfcd2e53d2950926f8c82c12afad02861c46fcb293e776204052725e1c08ff2e9ff602ca916357601fa972a14094891fe3598b718758f22c46f163c18bcaa6296ce87e5267ef3fd932112842fbbf79011548cdf067d93ce6098dfc0aaf5a94531e439f30d6dfd0c6"
));
}
}

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyTxDeposit`]
use crate::Compact;
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
use op_alloy_consensus::TxDeposit as AlloyTxDeposit;
@ -12,7 +14,11 @@ use reth_codecs_derive::add_arbitrary_tests;
///
/// Notice: Make sure this struct is 1:1 with [`op_alloy_consensus::TxDeposit`]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct TxDeposit {
source_hash: B256,

View File

@ -1,3 +1,5 @@
//! Compact implementation for [`AlloyWithdrawal`]
use crate::Compact;
use alloy_eips::eip4895::Withdrawal as AlloyWithdrawal;
use alloy_primitives::Address;
@ -7,7 +9,11 @@ use reth_codecs_derive::add_arbitrary_tests;
///
/// Notice: Make sure this struct is 1:1 with `alloy_eips::eip4895::Withdrawal`
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact)]
#[cfg_attr(test, derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize))]
#[cfg_attr(
any(test, feature = "test-utils"),
derive(arbitrary::Arbitrary, serde::Serialize, serde::Deserialize)
)]
#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
#[add_arbitrary_tests(compact)]
pub(crate) struct Withdrawal {
/// Monotonically increasing identifier issued by consensus layer.

View File

@ -18,6 +18,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
pub use reth_codecs_derive::*;
use serde as _;
use alloy_primitives::{Address, Bloom, Bytes, FixedBytes, U256};
use bytes::{Buf, BufMut};
@ -25,6 +26,10 @@ use bytes::{Buf, BufMut};
extern crate alloc;
use alloc::vec::Vec;
#[cfg(feature = "test-utils")]
pub mod alloy;
#[cfg(not(feature = "test-utils"))]
#[cfg(any(test, feature = "alloy"))]
mod alloy;

View File

@ -79,3 +79,12 @@ impl UnusedBits {
matches!(self, Self::NotZero)
}
}
/// Tests decoding and re-encoding to ensure correctness.
pub fn test_decode<T: crate::Compact>(buf: &[u8]) {
let (decoded, _) = T::from_compact(buf, buf.len());
let mut encoded = Vec::with_capacity(buf.len());
decoded.to_compact(&mut encoded);
assert_eq!(buf, &encoded[..]);
}