Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/consensus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ arbitrary = { workspace = true, features = ["derive"], optional = true }
# serde
alloy-serde = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde_with = { version = "3.9", optional = true }

# misc
spin.workspace = true
Expand All @@ -35,8 +36,10 @@ derive_more = { workspace = true, features = ["display"] }
alloy-primitives = { workspace = true, features = ["rand"] }
alloy-signer.workspace = true
arbitrary = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["macros"] }
bincode = "1.3"
rand.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["macros"] }

[features]
default = ["std"]
Expand All @@ -45,3 +48,4 @@ k256 = ["alloy-primitives/k256", "alloy-consensus/k256"]
kzg = ["alloy-eips/kzg", "alloy-consensus/kzg", "std"]
arbitrary = ["std", "dep:arbitrary", "alloy-consensus/arbitrary", "alloy-eips/arbitrary", "alloy-primitives/rand"]
serde = ["dep:serde", "dep:alloy-serde", "alloy-primitives/serde", "alloy-consensus/serde", "alloy-eips/serde"]
serde-bincode-compat = ["serde_with"]
12 changes: 12 additions & 0 deletions crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,15 @@ pub use hardforks::Hardforks;

mod block;
pub use block::OpBlock;

/// Bincode-compatible serde implementations for consensus types.
///
/// `bincode` crate doesn't work well with optionally serializable serde fields, but some of the
/// consensus 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::transaction::serde_bincode_compat::TxDeposit;
}
117 changes: 117 additions & 0 deletions crates/consensus/src/transaction/deposit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,120 @@ mod tests {
assert!(total_len > len_without_header);
}
}

/// Bincode-compatible [`TxDeposit`] serde implementation.
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub(super) mod serde_bincode_compat {
use alloc::borrow::Cow;
use alloy_primitives::{Address, Bytes, TxKind, B256, U256};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{DeserializeAs, SerializeAs};

/// Bincode-compatible [`super::TxDeposit`] serde implementation.
///
/// Intended to use with the [`serde_with::serde_as`] macro in the following way:
/// ```rust
/// use op_alloy_consensus::{serde_bincode_compat, TxDeposit};
/// use serde::{Deserialize, Serialize};
/// use serde_with::serde_as;
///
/// #[serde_as]
/// #[derive(Serialize, Deserialize)]
/// struct Data {
/// #[serde_as(as = "serde_bincode_compat::TxDeposit")]
/// transaction: TxDeposit,
/// }
/// ```
#[derive(Debug, Serialize, Deserialize)]
pub struct TxDeposit<'a> {
source_hash: B256,
from: Address,
#[serde(default)]
to: TxKind,
#[serde(default)]
mint: Option<u128>,
value: U256,
gas_limit: u64,
is_system_transaction: bool,
input: Cow<'a, Bytes>,
}

impl<'a> From<&'a super::TxDeposit> for TxDeposit<'a> {
fn from(value: &'a super::TxDeposit) -> Self {
Self {
source_hash: value.source_hash,
from: value.from,
to: value.to,
mint: value.mint,
value: value.value,
gas_limit: value.gas_limit,
is_system_transaction: value.is_system_transaction,
input: Cow::Borrowed(&value.input),
}
}
}

impl<'a> From<TxDeposit<'a>> for super::TxDeposit {
fn from(value: TxDeposit<'a>) -> Self {
Self {
source_hash: value.source_hash,
from: value.from,
to: value.to,
mint: value.mint,
value: value.value,
gas_limit: value.gas_limit,
is_system_transaction: value.is_system_transaction,
input: value.input.into_owned(),
}
}
}

impl<'a> SerializeAs<super::TxDeposit> for TxDeposit<'a> {
fn serialize_as<S>(source: &super::TxDeposit, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
TxDeposit::from(source).serialize(serializer)
}
}

impl<'de> DeserializeAs<'de, super::TxDeposit> for TxDeposit<'de> {
fn deserialize_as<D>(deserializer: D) -> Result<super::TxDeposit, D::Error>
where
D: Deserializer<'de>,
{
TxDeposit::deserialize(deserializer).map(Into::into)
}
}

#[cfg(test)]
mod tests {
use arbitrary::Arbitrary;
use rand::Rng;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

use super::super::{serde_bincode_compat, TxDeposit};

#[test]
fn test_tx_deposit_bincode_roundtrip() {
#[serde_as]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
struct Data {
#[serde_as(as = "serde_bincode_compat::TxDeposit")]
transaction: TxDeposit,
}

let mut bytes = [0u8; 1024];
rand::thread_rng().fill(bytes.as_mut_slice());
let data = Data {
transaction: TxDeposit::arbitrary(&mut arbitrary::Unstructured::new(&bytes))
.unwrap(),
};

let encoded = bincode::serialize(&data).unwrap();
let decoded: Data = bincode::deserialize(&encoded).unwrap();
assert_eq!(decoded, data);
}
}
}
6 changes: 6 additions & 0 deletions crates/consensus/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ pub use source::{
DepositSourceDomain, DepositSourceDomainIdentifier, L1InfoDepositSource, UpgradeDepositSource,
UserDepositSource,
};

/// Bincode-compatible serde implementations for transaction types.
#[cfg(all(feature = "serde", feature = "serde-bincode-compat"))]
pub(super) mod serde_bincode_compat {
pub use super::deposit::serde_bincode_compat::TxDeposit;
}