diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 76d9b0197..f401b0ef6 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -32,7 +32,7 @@ pub use sidecar::generate_blob_sidecar; #[cfg(feature = "c-kzg")] pub use sidecar::{BlobTransaction, BlobTransactionSidecar, BlobTransactionValidationError}; -pub use signature::Signature; +pub use signature::{extract_chain_id, Signature}; pub use tx_type::{ TxType, EIP1559_TX_TYPE_ID, EIP2930_TX_TYPE_ID, EIP4844_TX_TYPE_ID, LEGACY_TX_TYPE_ID, }; @@ -1740,18 +1740,9 @@ impl TryFrom for TransactionSignedEcRecovered { // If the transaction type is Legacy, adjust the v component of the // signature according to the Ethereum specification TxType::Legacy => { - // Calculate the new v value based on the EIP-155 formula: - // v = {0,1} + CHAIN_ID * 2 + 35 - !(signature.v - - U256::from(if let Some(chain_id) = transaction.chain_id() { - // If CHAIN_ID is available, calculate the new v value - // accordingly - chain_id.saturating_mul(2).saturating_add(35) - } else { - // If CHAIN_ID is not available, set v = {0,1} + 27 - 27 - })) - .is_zero() + extract_chain_id(signature.v.to()) + .map_err(|_| ConversionError::InvalidSignature)? + .0 } _ => !signature.v.is_zero(), } diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 8cd57dc7f..29db729e9 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -114,16 +114,11 @@ impl Signature { if v == 0 && r.is_zero() && s.is_zero() { return Ok((Self { r, s, odd_y_parity: false }, None)) } - return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")) } - let odd_y_parity = v == 28; - Ok((Self { r, s, odd_y_parity }, None)) - } else { - // EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35 - let odd_y_parity = ((v - 35) % 2) != 0; - let chain_id = (v - 35) >> 1; - Ok((Self { r, s, odd_y_parity }, Some(chain_id))) } + + let (odd_y_parity, chain_id) = extract_chain_id(v)?; + Ok((Self { r, s, odd_y_parity }, chain_id)) } /// Output the length of the signature without the length of the RLP header @@ -201,6 +196,24 @@ impl Signature { } } +/// Outputs (odd_y_parity, chain_id) from the `v` value. +/// This doesn't check validity of the `v` value for optimism. +#[inline] +pub fn extract_chain_id(v: u64) -> alloy_rlp::Result<(bool, Option)> { + if v < 35 { + // non-EIP-155 legacy scheme, v = 27 for even y-parity, v = 28 for odd y-parity + if v != 27 && v != 28 { + return Err(RlpError::Custom("invalid Ethereum signature (V is not 27 or 28)")) + } + Ok((v == 28, None)) + } else { + // EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35 + let odd_y_parity = ((v - 35) % 2) != 0; + let chain_id = (v - 35) >> 1; + Ok((odd_y_parity, Some(chain_id))) + } +} + #[cfg(test)] mod tests { use crate::{transaction::signature::SECP256K1N_HALF, Address, Signature, B256, U256};