mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat(exex, primitives): serde bincode compatibility (#11331)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
44
Cargo.lock
generated
44
Cargo.lock
generated
@ -124,6 +124,7 @@ dependencies = [
|
||||
"c-kzg",
|
||||
"derive_more",
|
||||
"serde",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5103,9 +5104,9 @@ checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-consensus"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "274ce39752bdd16614292484839eb3e62139724c15087d9175a5838dab8d6317"
|
||||
checksum = "c662868734bd5a274c4474dc0642b5211f008367e591573277e5895333cb78f5"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
@ -5115,14 +5116,15 @@ dependencies = [
|
||||
"arbitrary",
|
||||
"derive_more",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-genesis"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1691c004810c0cda7e429866a8c561f21a26649f4143db61b1ce4e390493ce2"
|
||||
checksum = "67b4faf4f93b34c263e66cb163a085d9da72ced1f3adb34b7bd70c6e9fc7e5d6"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
@ -5134,9 +5136,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-network"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f08eccaddff3ecf46c7c9850e4842ef6218481c6829b4135ce230610d0a8f679"
|
||||
checksum = "a51504fd83b75b5d5e09320a0b4657b3bf23fc8018d40038ebab4eafcd7b9a40"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-network",
|
||||
@ -5148,9 +5150,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-protocol"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20ca8f42c59b06ed0267e39279c3426576979b9e217db1d0f3f2e8f0c913fc01"
|
||||
checksum = "20bec4f5aff4fe44e1e5beecd988096e6b757bd4bdfe6b10bb3f08c410287348"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
@ -5165,9 +5167,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-rpc-types"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "118f7e47fa822356fe4529bfa3b5d828308c1b53769d2e268337fa5b7d357929"
|
||||
checksum = "971fb1d31a1764327e4cf27a5372d2fde5db8bead90f75a750eeab306979b34c"
|
||||
dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
@ -5182,9 +5184,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "op-alloy-rpc-types-engine"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a5cbbffe83cbec46f19b184b63270c2090ce72c200cff19bc29e1f47519952"
|
||||
checksum = "eb2b515967262eae36ccecf868ab123dd8a098476f08f28f8ab4c3db5e1ee306"
|
||||
dependencies = [
|
||||
"alloy-eips",
|
||||
"alloy-primitives",
|
||||
@ -7332,11 +7334,15 @@ version = "1.0.7"
|
||||
dependencies = [
|
||||
"alloy-eips",
|
||||
"alloy-primitives",
|
||||
"arbitrary",
|
||||
"bincode",
|
||||
"rand 0.8.5",
|
||||
"reth-execution-errors",
|
||||
"reth-primitives",
|
||||
"reth-trie",
|
||||
"revm",
|
||||
"serde",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -7421,8 +7427,14 @@ version = "1.0.7"
|
||||
dependencies = [
|
||||
"alloy-eips",
|
||||
"alloy-primitives",
|
||||
"reth-provider",
|
||||
"arbitrary",
|
||||
"bincode",
|
||||
"rand 0.8.5",
|
||||
"reth-chain-state",
|
||||
"reth-execution-types",
|
||||
"reth-primitives",
|
||||
"serde",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -8242,6 +8254,7 @@ dependencies = [
|
||||
"alloy-serde",
|
||||
"arbitrary",
|
||||
"assert_matches",
|
||||
"bincode",
|
||||
"bytes",
|
||||
"c-kzg",
|
||||
"criterion",
|
||||
@ -8262,11 +8275,13 @@ dependencies = [
|
||||
"reth-optimism-chainspec",
|
||||
"reth-primitives-traits",
|
||||
"reth-static-file-types",
|
||||
"reth-testing-utils",
|
||||
"reth-trie-common",
|
||||
"revm-primitives",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"test-fuzz",
|
||||
"zstd",
|
||||
]
|
||||
@ -8281,6 +8296,7 @@ dependencies = [
|
||||
"alloy-primitives",
|
||||
"alloy-rlp",
|
||||
"arbitrary",
|
||||
"bincode",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
@ -8289,10 +8305,12 @@ dependencies = [
|
||||
"proptest-arbitrary-interop",
|
||||
"rand 0.8.5",
|
||||
"reth-codecs",
|
||||
"reth-testing-utils",
|
||||
"revm-primitives",
|
||||
"roaring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with",
|
||||
"test-fuzz",
|
||||
]
|
||||
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@ -461,20 +461,21 @@ alloy-transport-ipc = { version = "0.4.0", default-features = false }
|
||||
alloy-transport-ws = { version = "0.4.0", default-features = false }
|
||||
|
||||
# op
|
||||
op-alloy-rpc-types = "0.3.1"
|
||||
op-alloy-rpc-types-engine = "0.3.1"
|
||||
op-alloy-network = "0.3.1"
|
||||
op-alloy-consensus = "0.3.1"
|
||||
op-alloy-rpc-types = "0.3.2"
|
||||
op-alloy-rpc-types-engine = "0.3.2"
|
||||
op-alloy-network = "0.3.2"
|
||||
op-alloy-consensus = "0.3.2"
|
||||
|
||||
# misc
|
||||
aquamarine = "0.5"
|
||||
auto_impl = "1"
|
||||
backon = "0.4"
|
||||
bincode = "1.3"
|
||||
bitflags = "2.4"
|
||||
boyer-moore-magiclen = "0.2.16"
|
||||
bytes = "1.5"
|
||||
clap = "4"
|
||||
cfg-if = "1.0"
|
||||
clap = "4"
|
||||
const_format = { version = "0.2.32", features = ["rust_1_64"] }
|
||||
dashmap = "6.0"
|
||||
derive_more = { version = "1", features = ["full"] }
|
||||
@ -589,7 +590,7 @@ tikv-jemalloc-ctl = "0.6"
|
||||
tikv-jemallocator = "0.6"
|
||||
tracy-client = "0.17.3"
|
||||
|
||||
#[patch.crates-io]
|
||||
[patch.crates-io]
|
||||
#alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"}
|
||||
#alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"}
|
||||
#alloy-genesis = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"}
|
||||
|
||||
@ -22,13 +22,18 @@ alloy-primitives.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
|
||||
serde = { workspace = true, optional = true }
|
||||
serde_with = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-primitives = { workspace = true, features = ["test-utils"] }
|
||||
alloy-eips.workspace = true
|
||||
arbitrary.workspace = true
|
||||
bincode.workspace = true
|
||||
rand.workspace = true
|
||||
reth-primitives = { workspace = true, features = ["test-utils"] }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
optimism = []
|
||||
serde = ["dep:serde", "reth-trie/serde", "revm/serde"]
|
||||
serde-bincode-compat = ["reth-primitives/serde-bincode-compat", "serde_with"]
|
||||
std = []
|
||||
|
||||
@ -723,3 +723,124 @@ mod tests {
|
||||
assert_eq!(chain.execution_outcome_at_block(11), Some(execution_outcome));
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`Chain`] serde implementation.
|
||||
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use alloy_primitives::BlockNumber;
|
||||
use reth_primitives::serde_bincode_compat::SealedBlockWithSenders;
|
||||
use reth_trie::updates::TrieUpdates;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
use crate::ExecutionOutcome;
|
||||
|
||||
/// Bincode-compatible [`super::Chain`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_execution_types::{serde_bincode_compat, Chain};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::Chain")]
|
||||
/// chain: Chain,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Chain<'a> {
|
||||
blocks: BTreeMap<BlockNumber, SealedBlockWithSenders<'a>>,
|
||||
execution_outcome: Cow<'a, ExecutionOutcome>,
|
||||
trie_updates: Cow<'a, Option<TrieUpdates>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::Chain> for Chain<'a> {
|
||||
fn from(value: &'a super::Chain) -> Self {
|
||||
Self {
|
||||
blocks: value
|
||||
.blocks
|
||||
.iter()
|
||||
.map(|(block_number, block)| (*block_number, block.into()))
|
||||
.collect(),
|
||||
execution_outcome: Cow::Borrowed(&value.execution_outcome),
|
||||
trie_updates: Cow::Borrowed(&value.trie_updates),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Chain<'a>> for super::Chain {
|
||||
fn from(value: Chain<'a>) -> Self {
|
||||
Self {
|
||||
blocks: value
|
||||
.blocks
|
||||
.into_iter()
|
||||
.map(|(block_number, block)| (block_number, block.into()))
|
||||
.collect(),
|
||||
execution_outcome: value.execution_outcome.into_owned(),
|
||||
trie_updates: value.trie_updates.into_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::Chain> for Chain<'a> {
|
||||
fn serialize_as<S>(source: &super::Chain, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Chain::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::Chain> for Chain<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::Chain, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Chain::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use arbitrary::Arbitrary;
|
||||
use rand::Rng;
|
||||
use reth_primitives::SealedBlockWithSenders;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
use super::super::{serde_bincode_compat, Chain};
|
||||
|
||||
#[test]
|
||||
fn test_chain_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::Chain")]
|
||||
chain: Chain,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
rand::thread_rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
chain: Chain::new(
|
||||
vec![SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new(
|
||||
&bytes,
|
||||
))
|
||||
.unwrap()],
|
||||
Default::default(),
|
||||
None,
|
||||
),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,3 +18,15 @@ pub use execute::*;
|
||||
|
||||
mod execution_outcome;
|
||||
pub use execution_outcome::*;
|
||||
|
||||
/// Bincode-compatible serde implementations for commonly used types for (EVM) block execution.
|
||||
///
|
||||
/// `bincode` crate doesn't work with optionally serializable serde fields, but some of the
|
||||
/// execution types require optional serialization for RPC compatibility. This module makes so that
|
||||
/// all fields are serialized.
|
||||
///
|
||||
/// Read more: <https://github.com/bincode-org/bincode/issues/326>
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub mod serde_bincode_compat {
|
||||
pub use super::chain::serde_bincode_compat::*;
|
||||
}
|
||||
|
||||
@ -13,7 +13,8 @@ workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-provider.workspace = true
|
||||
reth-chain-state.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
|
||||
# reth
|
||||
alloy-primitives.workspace = true
|
||||
@ -21,7 +22,16 @@ alloy-eips.workspace = true
|
||||
|
||||
# misc
|
||||
serde = { workspace = true, optional = true }
|
||||
serde_with = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-primitives = { workspace = true, features = ["arbitrary"] }
|
||||
|
||||
arbitrary.workspace = true
|
||||
bincode.workspace = true
|
||||
rand.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serde = ["dep:serde", "reth-provider/serde"]
|
||||
serde = ["dep:serde", "reth-execution-types/serde"]
|
||||
serde-bincode-compat = ["reth-execution-types/serde-bincode-compat", "serde_with"]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
//! Commonly used types for exex usage.
|
||||
//! Commonly used ExEx types.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
@ -15,3 +15,15 @@ mod notification;
|
||||
pub use finished_height::FinishedExExHeight;
|
||||
pub use head::ExExHead;
|
||||
pub use notification::ExExNotification;
|
||||
|
||||
/// Bincode-compatible serde implementations for commonly used ExEx types.
|
||||
///
|
||||
/// `bincode` crate doesn't work with optionally serializable serde fields, but some of the
|
||||
/// ExEx types require optional serialization for RPC compatibility. This module makes so that
|
||||
/// all fields are serialized.
|
||||
///
|
||||
/// Read more: <https://github.com/bincode-org/bincode/issues/326>
|
||||
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
|
||||
pub mod serde_bincode_compat {
|
||||
pub use super::notification::serde_bincode_compat::*;
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use reth_provider::{CanonStateNotification, Chain};
|
||||
use reth_chain_state::CanonStateNotification;
|
||||
use reth_execution_types::Chain;
|
||||
|
||||
/// Notifications sent to an `ExEx`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
@ -67,3 +68,143 @@ impl From<CanonStateNotification> for ExExNotification {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`ExExNotification`] serde implementation.
|
||||
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use std::sync::Arc;
|
||||
|
||||
use reth_execution_types::serde_bincode_compat::Chain;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`super::ExExNotification`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_exex_types::{serde_bincode_compat, ExExNotification};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::ExExNotification")]
|
||||
/// notification: ExExNotification,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ExExNotification<'a> {
|
||||
ChainCommitted { new: Chain<'a> },
|
||||
ChainReorged { old: Chain<'a>, new: Chain<'a> },
|
||||
ChainReverted { old: Chain<'a> },
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::ExExNotification> for ExExNotification<'a> {
|
||||
fn from(value: &'a super::ExExNotification) -> Self {
|
||||
match value {
|
||||
super::ExExNotification::ChainCommitted { new } => {
|
||||
ExExNotification::ChainCommitted { new: Chain::from(new.as_ref()) }
|
||||
}
|
||||
super::ExExNotification::ChainReorged { old, new } => {
|
||||
ExExNotification::ChainReorged {
|
||||
old: Chain::from(old.as_ref()),
|
||||
new: Chain::from(new.as_ref()),
|
||||
}
|
||||
}
|
||||
super::ExExNotification::ChainReverted { old } => {
|
||||
ExExNotification::ChainReverted { old: Chain::from(old.as_ref()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<ExExNotification<'a>> for super::ExExNotification {
|
||||
fn from(value: ExExNotification<'a>) -> Self {
|
||||
match value {
|
||||
ExExNotification::ChainCommitted { new } => {
|
||||
Self::ChainCommitted { new: Arc::new(new.into()) }
|
||||
}
|
||||
ExExNotification::ChainReorged { old, new } => {
|
||||
Self::ChainReorged { old: Arc::new(old.into()), new: Arc::new(new.into()) }
|
||||
}
|
||||
ExExNotification::ChainReverted { old } => {
|
||||
Self::ChainReverted { old: Arc::new(old.into()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::ExExNotification> for ExExNotification<'a> {
|
||||
fn serialize_as<S>(
|
||||
source: &super::ExExNotification,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
ExExNotification::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::ExExNotification> for ExExNotification<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::ExExNotification, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
ExExNotification::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use rand::Rng;
|
||||
use reth_execution_types::Chain;
|
||||
use reth_primitives::SealedBlockWithSenders;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
use super::super::{serde_bincode_compat, ExExNotification};
|
||||
|
||||
#[test]
|
||||
fn test_exex_notification_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::ExExNotification")]
|
||||
notification: ExExNotification,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
rand::thread_rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
notification: ExExNotification::ChainReorged {
|
||||
old: Arc::new(Chain::new(
|
||||
vec![SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new(
|
||||
&bytes,
|
||||
))
|
||||
.unwrap()],
|
||||
Default::default(),
|
||||
None,
|
||||
)),
|
||||
new: Arc::new(Chain::new(
|
||||
vec![SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new(
|
||||
&bytes,
|
||||
))
|
||||
.unwrap()],
|
||||
Default::default(),
|
||||
None,
|
||||
)),
|
||||
},
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,16 +20,17 @@ alloy-genesis.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
derive_more.workspace = true
|
||||
revm-primitives = { workspace = true, features = ["serde"] }
|
||||
|
||||
# misc
|
||||
roaring = "0.10.2"
|
||||
byteorder = "1"
|
||||
derive_more.workspace = true
|
||||
roaring = "0.10.2"
|
||||
serde_with = { workspace = true, optional = true }
|
||||
|
||||
# required by reth-codecs
|
||||
modular-bitfield.workspace = true
|
||||
bytes.workspace = true
|
||||
modular-bitfield.workspace = true
|
||||
serde.workspace = true
|
||||
|
||||
# arbitrary utils
|
||||
@ -38,14 +39,18 @@ proptest = { workspace = true, optional = true }
|
||||
proptest-arbitrary-interop = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
reth-testing-utils.workspace = true
|
||||
|
||||
alloy-primitives = { workspace = true, features = ["arbitrary"] }
|
||||
alloy-consensus = { workspace = true, features = ["arbitrary"] }
|
||||
|
||||
arbitrary = { workspace = true, features = ["derive"] }
|
||||
proptest.workspace = true
|
||||
bincode.workspace = true
|
||||
proptest-arbitrary-interop.workspace = true
|
||||
test-fuzz.workspace = true
|
||||
proptest.workspace = true
|
||||
rand.workspace = true
|
||||
serde_json.workspace = true
|
||||
test-fuzz.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@ -59,3 +64,4 @@ arbitrary = [
|
||||
"dep:proptest",
|
||||
"dep:proptest-arbitrary-interop",
|
||||
]
|
||||
serde-bincode-compat = ["serde_with", "alloy-consensus/serde-bincode-compat"]
|
||||
|
||||
@ -11,6 +11,11 @@ pub use alloy_consensus::Header;
|
||||
|
||||
use alloy_primitives::{Address, BlockNumber, B256, U256};
|
||||
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
pub use super::sealed::serde_bincode_compat::SealedHeader;
|
||||
}
|
||||
|
||||
/// Trait for extracting specific Ethereum block data from a header
|
||||
pub trait BlockHeader {
|
||||
/// Retrieves the beneficiary (miner) of the block
|
||||
|
||||
@ -140,3 +140,95 @@ impl<'a> arbitrary::Arbitrary<'a> for SealedHeader {
|
||||
Ok(Self::new(header, seal))
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`SealedHeader`] serde implementation.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use alloy_consensus::serde_bincode_compat::Header;
|
||||
use alloy_primitives::BlockHash;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`super::SealedHeader`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives_traits::{header::SealedHeader, serde_bincode_compat};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::header::SealedHeader")]
|
||||
/// header: SealedHeader,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SealedHeader<'a> {
|
||||
hash: BlockHash,
|
||||
header: Header<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::SealedHeader> for SealedHeader<'a> {
|
||||
fn from(value: &'a super::SealedHeader) -> Self {
|
||||
Self { hash: value.hash, header: Header::from(&value.header) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<SealedHeader<'a>> for super::SealedHeader {
|
||||
fn from(value: SealedHeader<'a>) -> Self {
|
||||
Self { hash: value.hash, header: value.header.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::SealedHeader> for SealedHeader<'a> {
|
||||
fn serialize_as<S>(source: &super::SealedHeader, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SealedHeader::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::SealedHeader> for SealedHeader<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::SealedHeader, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
SealedHeader::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{serde_bincode_compat, SealedHeader};
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use rand::Rng;
|
||||
use reth_testing_utils::generators;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
#[test]
|
||||
fn test_sealed_header_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::SealedHeader")]
|
||||
transaction: SealedHeader,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
transaction: SealedHeader::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
//! Common abstracted types in reth.
|
||||
//! Common abstracted types in Reth.
|
||||
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
|
||||
@ -43,3 +43,15 @@ pub mod header;
|
||||
#[cfg(any(test, feature = "arbitrary", feature = "test-utils"))]
|
||||
pub use header::test_utils;
|
||||
pub use header::{BlockHeader, Header, HeaderError, SealedHeader};
|
||||
|
||||
/// Bincode-compatible serde implementations for common abstracted types in Reth.
|
||||
///
|
||||
/// `bincode` crate doesn't work with optionally serializable serde fields, but some of the
|
||||
/// Reth types require optional serialization for RPC compatibility. This module makes so that
|
||||
/// all fields are serialized.
|
||||
///
|
||||
/// Read more: <https://github.com/bincode-org/bincode/issues/326>
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub mod serde_bincode_compat {
|
||||
pub use super::header::serde_bincode_compat::*;
|
||||
}
|
||||
|
||||
@ -51,9 +51,11 @@ c-kzg = { workspace = true, features = ["serde"], optional = true }
|
||||
bytes.workspace = true
|
||||
derive_more.workspace = true
|
||||
modular-bitfield = { workspace = true, optional = true }
|
||||
once_cell.workspace = true
|
||||
rand = { workspace = true, optional = true }
|
||||
rayon.workspace = true
|
||||
serde.workspace = true
|
||||
once_cell.workspace = true
|
||||
serde_with = { workspace = true, optional = true }
|
||||
zstd = { workspace = true, features = ["experimental"], optional = true }
|
||||
|
||||
# arbitrary utils
|
||||
@ -62,22 +64,24 @@ proptest = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# eth
|
||||
reth-primitives-traits = { workspace = true, features = ["arbitrary"] }
|
||||
revm-primitives = { workspace = true, features = ["arbitrary"] }
|
||||
reth-chainspec.workspace = true
|
||||
reth-codecs.workspace = true
|
||||
reth-primitives-traits = { workspace = true, features = ["arbitrary"] }
|
||||
reth-testing-utils.workspace = true
|
||||
revm-primitives = { workspace = true, features = ["arbitrary"] }
|
||||
|
||||
alloy-eips = { workspace = true, features = ["arbitrary"] }
|
||||
alloy-genesis.workspace = true
|
||||
|
||||
assert_matches.workspace = true
|
||||
arbitrary = { workspace = true, features = ["derive"] }
|
||||
proptest.workspace = true
|
||||
assert_matches.workspace = true
|
||||
bincode.workspace = true
|
||||
modular-bitfield.workspace = true
|
||||
proptest-arbitrary-interop.workspace = true
|
||||
proptest.workspace = true
|
||||
rand.workspace = true
|
||||
serde_json.workspace = true
|
||||
test-fuzz.workspace = true
|
||||
modular-bitfield.workspace = true
|
||||
|
||||
|
||||
criterion.workspace = true
|
||||
pprof = { workspace = true, features = [
|
||||
@ -92,26 +96,28 @@ std = ["reth-primitives-traits/std"]
|
||||
reth-codec = ["dep:reth-codecs", "dep:zstd", "dep:modular-bitfield", "std"]
|
||||
asm-keccak = ["alloy-primitives/asm-keccak"]
|
||||
arbitrary = [
|
||||
"reth-primitives-traits/arbitrary",
|
||||
"revm-primitives/arbitrary",
|
||||
"reth-ethereum-forks/arbitrary",
|
||||
"alloy-eips/arbitrary",
|
||||
"dep:arbitrary",
|
||||
"dep:proptest",
|
||||
"alloy-eips/arbitrary",
|
||||
"rand",
|
||||
"reth-codec",
|
||||
"reth-ethereum-forks/arbitrary",
|
||||
"reth-primitives-traits/arbitrary",
|
||||
"revm-primitives/arbitrary",
|
||||
"secp256k1",
|
||||
]
|
||||
secp256k1 = ["dep:secp256k1"]
|
||||
c-kzg = [
|
||||
"dep:c-kzg",
|
||||
"revm-primitives/c-kzg",
|
||||
"alloy-eips/kzg",
|
||||
"alloy-consensus/kzg",
|
||||
"alloy-eips/kzg",
|
||||
"revm-primitives/c-kzg",
|
||||
]
|
||||
optimism = [
|
||||
"revm-primitives/optimism",
|
||||
"reth-codecs?/optimism",
|
||||
"dep:reth-optimism-chainspec",
|
||||
"dep:op-alloy-consensus",
|
||||
"dep:reth-optimism-chainspec",
|
||||
"reth-codecs?/optimism",
|
||||
"revm-primitives/optimism",
|
||||
]
|
||||
alloy-compat = [
|
||||
"dep:alloy-rpc-types",
|
||||
@ -119,6 +125,12 @@ alloy-compat = [
|
||||
"dep:op-alloy-rpc-types",
|
||||
]
|
||||
test-utils = ["reth-primitives-traits/test-utils"]
|
||||
serde-bincode-compat = [
|
||||
"alloy-consensus/serde-bincode-compat",
|
||||
"op-alloy-consensus?/serde-bincode-compat",
|
||||
"reth-primitives-traits/serde-bincode-compat",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[bench]]
|
||||
name = "recover_ecdsa_crit"
|
||||
|
||||
@ -541,6 +541,22 @@ impl SealedBlockWithSenders {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for SealedBlockWithSenders {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
let block = SealedBlock::arbitrary(u)?;
|
||||
|
||||
let senders = block
|
||||
.body
|
||||
.transactions
|
||||
.iter()
|
||||
.map(|tx| tx.recover_signer().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(Self { block, senders })
|
||||
}
|
||||
}
|
||||
|
||||
/// A response to `GetBlockBodies`, containing bodies if any bodies were found.
|
||||
///
|
||||
/// Withdrawals can be optionally included at the end of the RLP encoded message.
|
||||
@ -861,3 +877,257 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible block type serde implementations.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub(super) mod serde_bincode_compat {
|
||||
use alloc::{borrow::Cow, vec::Vec};
|
||||
use alloy_consensus::serde_bincode_compat::Header;
|
||||
use alloy_primitives::Address;
|
||||
use reth_primitives_traits::{serde_bincode_compat::SealedHeader, Requests, Withdrawals};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
use crate::transaction::serde_bincode_compat::TransactionSigned;
|
||||
|
||||
/// Bincode-compatible [`super::BlockBody`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives::{serde_bincode_compat, BlockBody};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::BlockBody")]
|
||||
/// body: BlockBody,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct BlockBody<'a> {
|
||||
transactions: Vec<TransactionSigned<'a>>,
|
||||
ommers: Vec<Header<'a>>,
|
||||
withdrawals: Cow<'a, Option<Withdrawals>>,
|
||||
requests: Cow<'a, Option<Requests>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::BlockBody> for BlockBody<'a> {
|
||||
fn from(value: &'a super::BlockBody) -> Self {
|
||||
Self {
|
||||
transactions: value.transactions.iter().map(Into::into).collect(),
|
||||
ommers: value.ommers.iter().map(Into::into).collect(),
|
||||
withdrawals: Cow::Borrowed(&value.withdrawals),
|
||||
requests: Cow::Borrowed(&value.requests),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<BlockBody<'a>> for super::BlockBody {
|
||||
fn from(value: BlockBody<'a>) -> Self {
|
||||
Self {
|
||||
transactions: value.transactions.into_iter().map(Into::into).collect(),
|
||||
ommers: value.ommers.into_iter().map(Into::into).collect(),
|
||||
withdrawals: value.withdrawals.into_owned(),
|
||||
requests: value.requests.into_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::BlockBody> for BlockBody<'a> {
|
||||
fn serialize_as<S>(source: &super::BlockBody, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
BlockBody::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::BlockBody> for BlockBody<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::BlockBody, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
BlockBody::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`super::SealedBlock`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives::{serde_bincode_compat, SealedBlock};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::SealedBlock")]
|
||||
/// block: SealedBlock,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SealedBlock<'a> {
|
||||
header: SealedHeader<'a>,
|
||||
body: BlockBody<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::SealedBlock> for SealedBlock<'a> {
|
||||
fn from(value: &'a super::SealedBlock) -> Self {
|
||||
Self { header: SealedHeader::from(&value.header), body: BlockBody::from(&value.body) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<SealedBlock<'a>> for super::SealedBlock {
|
||||
fn from(value: SealedBlock<'a>) -> Self {
|
||||
Self { header: value.header.into(), body: value.body.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::SealedBlock> for SealedBlock<'a> {
|
||||
fn serialize_as<S>(source: &super::SealedBlock, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SealedBlock::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::SealedBlock> for SealedBlock<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::SealedBlock, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
SealedBlock::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`super::SealedBlockWithSenders`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives::{serde_bincode_compat, SealedBlockWithSenders};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::SealedBlockWithSenders")]
|
||||
/// block: SealedBlockWithSenders,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SealedBlockWithSenders<'a> {
|
||||
block: SealedBlock<'a>,
|
||||
senders: Cow<'a, Vec<Address>>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::SealedBlockWithSenders> for SealedBlockWithSenders<'a> {
|
||||
fn from(value: &'a super::SealedBlockWithSenders) -> Self {
|
||||
Self { block: SealedBlock::from(&value.block), senders: Cow::Borrowed(&value.senders) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<SealedBlockWithSenders<'a>> for super::SealedBlockWithSenders {
|
||||
fn from(value: SealedBlockWithSenders<'a>) -> Self {
|
||||
Self { block: value.block.into(), senders: value.senders.into_owned() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::SealedBlockWithSenders> for SealedBlockWithSenders<'a> {
|
||||
fn serialize_as<S>(
|
||||
source: &super::SealedBlockWithSenders,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
SealedBlockWithSenders::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::SealedBlockWithSenders> for SealedBlockWithSenders<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::SealedBlockWithSenders, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
SealedBlockWithSenders::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{serde_bincode_compat, BlockBody, SealedBlock, SealedBlockWithSenders};
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use rand::Rng;
|
||||
use reth_testing_utils::generators;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
#[test]
|
||||
fn test_block_body_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::BlockBody")]
|
||||
block_body: BlockBody,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
block_body: BlockBody::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sealed_block_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::SealedBlock")]
|
||||
block: SealedBlock,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
block: SealedBlock::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sealed_block_with_senders_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::SealedBlockWithSenders")]
|
||||
block: SealedBlockWithSenders,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
block: SealedBlockWithSenders::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
//! Commonly used types in reth.
|
||||
//! Commonly used types in Reth.
|
||||
//!
|
||||
//! This crate contains Ethereum primitive types and helper functions.
|
||||
//!
|
||||
@ -87,3 +87,18 @@ mod optimism {
|
||||
|
||||
#[cfg(feature = "optimism")]
|
||||
pub use optimism::*;
|
||||
|
||||
/// Bincode-compatible serde implementations for commonly used types in Reth.
|
||||
///
|
||||
/// `bincode` crate doesn't work with optionally serializable serde fields, but some of the
|
||||
/// Reth types require optional serialization for RPC compatibility. This module makes so that
|
||||
/// all fields are serialized.
|
||||
///
|
||||
/// Read more: <https://github.com/bincode-org/bincode/issues/326>
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub mod serde_bincode_compat {
|
||||
pub use super::{
|
||||
block::serde_bincode_compat::*,
|
||||
transaction::{serde_bincode_compat as transaction, serde_bincode_compat::*},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1416,7 +1416,14 @@ impl<'a> arbitrary::Arbitrary<'a> for TransactionSigned {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
#[allow(unused_mut)]
|
||||
let mut transaction = Transaction::arbitrary(u)?;
|
||||
let mut signature = Signature::arbitrary(u)?;
|
||||
|
||||
let secp = secp256k1::Secp256k1::new();
|
||||
let key_pair = secp256k1::Keypair::new(&secp, &mut rand::thread_rng());
|
||||
let mut signature = crate::sign_message(
|
||||
B256::from_slice(&key_pair.secret_bytes()[..]),
|
||||
transaction.signature_hash(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
signature = if matches!(transaction, Transaction::Legacy(_)) {
|
||||
if let Some(chain_id) = transaction.chain_id() {
|
||||
@ -1969,3 +1976,210 @@ mod tests {
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible transaction type serde implementations.
|
||||
#[cfg(feature = "serde-bincode-compat")]
|
||||
pub mod serde_bincode_compat {
|
||||
use alloc::borrow::Cow;
|
||||
use alloy_consensus::{
|
||||
transaction::serde_bincode_compat::{TxEip1559, TxEip2930, TxLegacy},
|
||||
TxEip4844, TxEip7702,
|
||||
};
|
||||
use alloy_primitives::{Signature, TxHash};
|
||||
#[cfg(feature = "optimism")]
|
||||
use op_alloy_consensus::serde_bincode_compat::TxDeposit;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde_with::{DeserializeAs, SerializeAs};
|
||||
|
||||
/// Bincode-compatible [`super::Transaction`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives_traits::{serde_bincode_compat, Transaction};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::transaction::Transaction")]
|
||||
/// transaction: Transaction,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Transaction<'a> {
|
||||
Legacy(TxLegacy<'a>),
|
||||
Eip2930(TxEip2930<'a>),
|
||||
Eip1559(TxEip1559<'a>),
|
||||
Eip4844(Cow<'a, TxEip4844>),
|
||||
Eip7702(Cow<'a, TxEip7702>),
|
||||
#[cfg(feature = "optimism")]
|
||||
#[cfg(feature = "optimism")]
|
||||
Deposit(TxDeposit<'a>),
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::Transaction> for Transaction<'a> {
|
||||
fn from(value: &'a super::Transaction) -> Self {
|
||||
match value {
|
||||
super::Transaction::Legacy(tx) => Self::Legacy(TxLegacy::from(tx)),
|
||||
super::Transaction::Eip2930(tx) => Self::Eip2930(TxEip2930::from(tx)),
|
||||
super::Transaction::Eip1559(tx) => Self::Eip1559(TxEip1559::from(tx)),
|
||||
super::Transaction::Eip4844(tx) => Self::Eip4844(Cow::Borrowed(tx)),
|
||||
super::Transaction::Eip7702(tx) => Self::Eip7702(Cow::Borrowed(tx)),
|
||||
#[cfg(feature = "optimism")]
|
||||
super::Transaction::Deposit(tx) => Self::Deposit(TxDeposit::from(tx)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Transaction<'a>> for super::Transaction {
|
||||
fn from(value: Transaction<'a>) -> Self {
|
||||
match value {
|
||||
Transaction::Legacy(tx) => Self::Legacy(tx.into()),
|
||||
Transaction::Eip2930(tx) => Self::Eip2930(tx.into()),
|
||||
Transaction::Eip1559(tx) => Self::Eip1559(tx.into()),
|
||||
Transaction::Eip4844(tx) => Self::Eip4844(tx.into_owned()),
|
||||
Transaction::Eip7702(tx) => Self::Eip7702(tx.into_owned()),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(tx) => Self::Deposit(tx.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::Transaction> for Transaction<'a> {
|
||||
fn serialize_as<S>(source: &super::Transaction, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
Transaction::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::Transaction> for Transaction<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::Transaction, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Transaction::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Bincode-compatible [`super::TransactionSigned`] serde implementation.
|
||||
///
|
||||
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
|
||||
/// ```rust
|
||||
/// use reth_primitives_traits::{serde_bincode_compat, TransactionSigned};
|
||||
/// use serde::{Deserialize, Serialize};
|
||||
/// use serde_with::serde_as;
|
||||
///
|
||||
/// #[serde_as]
|
||||
/// #[derive(Serialize, Deserialize)]
|
||||
/// struct Data {
|
||||
/// #[serde_as(as = "serde_bincode_compat::transaction::TransactionSigned")]
|
||||
/// transaction: TransactionSigned,
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct TransactionSigned<'a> {
|
||||
hash: TxHash,
|
||||
signature: Signature,
|
||||
transaction: Transaction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a super::TransactionSigned> for TransactionSigned<'a> {
|
||||
fn from(value: &'a super::TransactionSigned) -> Self {
|
||||
Self {
|
||||
hash: value.hash,
|
||||
signature: value.signature,
|
||||
transaction: Transaction::from(&value.transaction),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<TransactionSigned<'a>> for super::TransactionSigned {
|
||||
fn from(value: TransactionSigned<'a>) -> Self {
|
||||
Self {
|
||||
hash: value.hash,
|
||||
signature: value.signature,
|
||||
transaction: value.transaction.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> SerializeAs<super::TransactionSigned> for TransactionSigned<'a> {
|
||||
fn serialize_as<S>(
|
||||
source: &super::TransactionSigned,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
TransactionSigned::from(source).serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> DeserializeAs<'de, super::TransactionSigned> for TransactionSigned<'de> {
|
||||
fn deserialize_as<D>(deserializer: D) -> Result<super::TransactionSigned, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
TransactionSigned::deserialize(deserializer).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{serde_bincode_compat, Transaction, TransactionSigned};
|
||||
|
||||
use arbitrary::Arbitrary;
|
||||
use rand::Rng;
|
||||
use reth_testing_utils::generators;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_with::serde_as;
|
||||
|
||||
#[test]
|
||||
fn test_transaction_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::Transaction")]
|
||||
transaction: Transaction,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
transaction: Transaction::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transaction_signed_bincode_roundtrip() {
|
||||
#[serde_as]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
struct Data {
|
||||
#[serde_as(as = "serde_bincode_compat::TransactionSigned")]
|
||||
transaction: TransactionSigned,
|
||||
}
|
||||
|
||||
let mut bytes = [0u8; 1024];
|
||||
generators::rng().fill(bytes.as_mut_slice());
|
||||
let data = Data {
|
||||
transaction: TransactionSigned::arbitrary(&mut arbitrary::Unstructured::new(
|
||||
&bytes,
|
||||
))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&data).unwrap();
|
||||
let decoded: Data = bincode::deserialize(&encoded).unwrap();
|
||||
assert_eq!(decoded, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ zstd = { workspace = true, features = ["experimental", "zdict_builder"] }
|
||||
lz4_flex = { version = "0.11", default-features = false }
|
||||
|
||||
memmap2 = "0.9.4"
|
||||
bincode = "1.3"
|
||||
bincode.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tracing.workspace = true
|
||||
anyhow = "1.0"
|
||||
|
||||
Reference in New Issue
Block a user