From 0dc2db66a460670bc51dd2219674dfd289b3bc9e Mon Sep 17 00:00:00 2001 From: James Date: Tue, 15 Oct 2024 17:44:53 -0400 Subject: [PATCH] fix: allow missing-tag deser of tx envelope --- crates/consensus/src/transaction/envelope.rs | 81 ++++++++++++++++++-- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/crates/consensus/src/transaction/envelope.rs b/crates/consensus/src/transaction/envelope.rs index 8fbfe21a5d9..aad9dd70b1d 100644 --- a/crates/consensus/src/transaction/envelope.rs +++ b/crates/consensus/src/transaction/envelope.rs @@ -87,18 +87,18 @@ impl TryFrom for TxType { /// [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "serde", serde(tag = "type"))] +#[cfg_attr( + feature = "serde", + serde(into = "serde_from::TaggedTxEnvelope", from = "serde_from::MaybeTaggedTxEnvelope") +)] #[doc(alias = "TransactionEnvelope")] #[non_exhaustive] pub enum TxEnvelope { /// An untagged [`TxLegacy`]. - #[cfg_attr(feature = "serde", serde(rename = "0x0", alias = "0x00"))] Legacy(Signed), /// A [`TxEip2930`] tagged with type 1. - #[cfg_attr(feature = "serde", serde(rename = "0x1", alias = "0x01"))] Eip2930(Signed), /// A [`TxEip1559`] tagged with type 2. - #[cfg_attr(feature = "serde", serde(rename = "0x2", alias = "0x02"))] Eip1559(Signed), /// A TxEip4844 tagged with type 3. /// An EIP-4844 transaction has two network representations: @@ -107,10 +107,8 @@ pub enum TxEnvelope { /// /// 2 - The transaction with a sidecar, which is the form used to /// send transactions to the network. - #[cfg_attr(feature = "serde", serde(rename = "0x3", alias = "0x03"))] Eip4844(Signed), /// A [`TxEip7702`] tagged with type 4. - #[cfg_attr(feature = "serde", serde(rename = "0x4", alias = "0x04"))] Eip7702(Signed), } @@ -518,6 +516,77 @@ impl Transaction for TxEnvelope { } } +#[cfg(feature = "serde")] +mod serde_from { + //! NB: Why do we need this? + //! + //! Because the tag may be missing, we need an abstraction over tagged (with + //! type) and untagged (always legacy). This is [`MaybeTaggedTxEnvelope`]. + //! + //! The tagged variant is [`TaggedTxEnvelope`], which always has a type tag. + //! + //! We serialize via [`TaggedTxEnvelope`] and deserialize via + //! [`MaybeTaggedTxEnvelope`]. + use crate::{Signed, TxEip1559, TxEip2930, TxEip4844Variant, TxEip7702, TxLegacy}; + + use super::TxEnvelope; + + #[derive(Debug, serde::Deserialize)] + #[serde(untagged)] + pub(crate) enum MaybeTaggedTxEnvelope { + Tagged(TaggedTxEnvelope), + Untagged(Signed), + } + + #[derive(Debug, serde::Serialize, serde::Deserialize)] + #[serde(tag = "type")] + pub(crate) enum TaggedTxEnvelope { + #[serde(rename = "0x0", alias = "0x00")] + Legacy(Signed), + #[serde(rename = "0x1", alias = "0x01")] + Eip2930(Signed), + #[serde(rename = "0x2", alias = "0x02")] + Eip1559(Signed), + #[serde(rename = "0x3", alias = "0x03")] + Eip4844(Signed), + #[serde(rename = "0x4", alias = "0x04")] + Eip7702(Signed), + } + + impl From for TxEnvelope { + fn from(value: MaybeTaggedTxEnvelope) -> Self { + match value { + MaybeTaggedTxEnvelope::Tagged(tagged) => tagged.into(), + MaybeTaggedTxEnvelope::Untagged(tx) => Self::Legacy(tx), + } + } + } + + impl From for TxEnvelope { + fn from(value: TaggedTxEnvelope) -> Self { + match value { + TaggedTxEnvelope::Legacy(signed) => Self::Legacy(signed), + TaggedTxEnvelope::Eip2930(signed) => Self::Eip2930(signed), + TaggedTxEnvelope::Eip1559(signed) => Self::Eip1559(signed), + TaggedTxEnvelope::Eip4844(signed) => Self::Eip4844(signed), + TaggedTxEnvelope::Eip7702(signed) => Self::Eip7702(signed), + } + } + } + + impl From for TaggedTxEnvelope { + fn from(value: TxEnvelope) -> Self { + match value { + TxEnvelope::Legacy(signed) => Self::Legacy(signed), + TxEnvelope::Eip2930(signed) => Self::Eip2930(signed), + TxEnvelope::Eip1559(signed) => Self::Eip1559(signed), + TxEnvelope::Eip4844(signed) => Self::Eip4844(signed), + TxEnvelope::Eip7702(signed) => Self::Eip7702(signed), + } + } + } +} + #[cfg(test)] mod tests { use super::*;