mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore(sdk): define OpTransactionSigned (#11433)
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -8464,14 +8464,20 @@ dependencies = [
|
||||
"alloy-consensus",
|
||||
"alloy-eips",
|
||||
"alloy-primitives",
|
||||
"alloy-rlp",
|
||||
"arbitrary",
|
||||
"bytes",
|
||||
"derive_more 1.0.0",
|
||||
"op-alloy-consensus",
|
||||
"proptest",
|
||||
"proptest-arbitrary-interop",
|
||||
"rand 0.8.5",
|
||||
"reth-codecs",
|
||||
"reth-primitives",
|
||||
"reth-primitives-traits",
|
||||
"revm-primitives",
|
||||
"rstest",
|
||||
"secp256k1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
|
||||
@ -44,7 +44,8 @@ optimism = [
|
||||
"reth-optimism-evm/optimism",
|
||||
"reth-optimism-payload-builder/optimism",
|
||||
"reth-optimism-rpc/optimism",
|
||||
"reth-provider/optimism"
|
||||
"reth-provider/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
]
|
||||
|
||||
dev = [
|
||||
|
||||
@ -95,6 +95,7 @@ optimism = [
|
||||
"reth-execution-types/optimism",
|
||||
"reth-db/optimism",
|
||||
"reth-db-api/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
"reth-downloaders/optimism"
|
||||
]
|
||||
asm-keccak = [
|
||||
|
||||
@ -64,6 +64,7 @@ std = [
|
||||
"alloy-primitives/std",
|
||||
"revm-primitives/std",
|
||||
"revm/std",
|
||||
"reth-optimism-primitives/std",
|
||||
"reth-ethereum-forks/std",
|
||||
"derive_more/std",
|
||||
"reth-optimism-forks/std"
|
||||
@ -73,5 +74,6 @@ optimism = [
|
||||
"reth-execution-types/optimism",
|
||||
"reth-optimism-consensus/optimism",
|
||||
"revm/optimism",
|
||||
"revm-primitives/optimism"
|
||||
"revm-primitives/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
]
|
||||
|
||||
@ -41,7 +41,7 @@ reth-optimism-rpc.workspace = true
|
||||
reth-optimism-chainspec.workspace = true
|
||||
reth-optimism-consensus.workspace = true
|
||||
reth-optimism-forks.workspace = true
|
||||
reth-optimism-primitives.workspace = true
|
||||
reth-optimism-primitives = { workspace = true, features = ["serde"] }
|
||||
|
||||
# revm with required optimism features
|
||||
revm = { workspace = true, features = ["secp256k1", "blst", "c-kzg"] }
|
||||
@ -96,7 +96,8 @@ optimism = [
|
||||
"reth-optimism-consensus/optimism",
|
||||
"reth-db/optimism",
|
||||
"reth-optimism-node/optimism",
|
||||
"reth-node-core/optimism"
|
||||
"reth-node-core/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
]
|
||||
asm-keccak = [
|
||||
"reth-primitives/asm-keccak",
|
||||
|
||||
@ -20,45 +20,60 @@ reth-codecs = { workspace = true, optional = true, features = ["op"] }
|
||||
# ethereum
|
||||
alloy-primitives.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
alloy-rlp.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
revm-primitives.workspace = true
|
||||
secp256k1 = { workspace = true, optional = true }
|
||||
|
||||
# op
|
||||
op-alloy-consensus.workspace = true
|
||||
|
||||
# codec
|
||||
bytes.workspace = true
|
||||
bytes = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
# misc
|
||||
derive_more.workspace = true
|
||||
derive_more = { workspace = true, features = ["deref", "from", "into", "constructor"] }
|
||||
rand = { workspace = true, optional = true }
|
||||
|
||||
# test
|
||||
arbitrary = { workspace = true, features = ["derive"], optional = true }
|
||||
proptest = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
proptest-arbitrary-interop.workspace = true
|
||||
reth-codecs = { workspace = true, features = ["test-utils", "op"] }
|
||||
rstest.workspace = true
|
||||
arbitrary.workspace = true
|
||||
proptest.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["std", "reth-codec"]
|
||||
default = ["std"]
|
||||
std = [
|
||||
"reth-primitives-traits/std",
|
||||
"reth-primitives/std",
|
||||
"reth-codecs/std",
|
||||
"reth-codecs?/std",
|
||||
"alloy-consensus/std",
|
||||
"alloy-eips/std",
|
||||
"alloy-primitives/std",
|
||||
"serde/std",
|
||||
"bytes/std",
|
||||
"derive_more/std"
|
||||
"serde?/std",
|
||||
"bytes?/std",
|
||||
"derive_more/std",
|
||||
"revm-primitives/std",
|
||||
"secp256k1?/std",
|
||||
"alloy-rlp/std",
|
||||
]
|
||||
reth-codec = [
|
||||
"dep:reth-codecs",
|
||||
"std",
|
||||
"rand",
|
||||
"dep:proptest",
|
||||
"dep:arbitrary",
|
||||
"reth-primitives/reth-codec",
|
||||
"reth-primitives-traits/reth-codec",
|
||||
"reth-codecs?/op",
|
||||
"reth-primitives/reth-codec"
|
||||
"reth-primitives/reth-codec",
|
||||
"dep:bytes",
|
||||
]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
@ -66,12 +81,16 @@ serde = [
|
||||
"alloy-primitives/serde",
|
||||
"alloy-consensus/serde",
|
||||
"alloy-eips/serde",
|
||||
"bytes/serde",
|
||||
"bytes?/serde",
|
||||
"reth-codecs?/serde",
|
||||
"op-alloy-consensus/serde",
|
||||
"rand?/serde",
|
||||
"revm-primitives/serde",
|
||||
"secp256k1?/serde",
|
||||
]
|
||||
arbitrary = [
|
||||
"dep:arbitrary",
|
||||
"dep:secp256k1",
|
||||
"reth-primitives-traits/arbitrary",
|
||||
"reth-primitives/arbitrary",
|
||||
"reth-codecs?/arbitrary",
|
||||
@ -79,4 +98,9 @@ arbitrary = [
|
||||
"alloy-consensus/arbitrary",
|
||||
"alloy-eips/arbitrary",
|
||||
"alloy-primitives/arbitrary",
|
||||
"revm-primitives/arbitrary",
|
||||
"rand",
|
||||
]
|
||||
op = [
|
||||
"revm-primitives/optimism",
|
||||
]
|
||||
|
||||
@ -6,17 +6,20 @@
|
||||
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
// The `optimism` feature must be enabled to use this crate.
|
||||
#![cfg(feature = "op")]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod bedrock;
|
||||
pub mod transaction;
|
||||
|
||||
use reth_primitives::EthPrimitives;
|
||||
pub use transaction::{tx_type::OpTxType, OpTransaction};
|
||||
pub use transaction::{signed::OpTransactionSigned, tx_type::OpTxType, OpTransaction};
|
||||
|
||||
/// Optimism primitive types.
|
||||
pub type OpPrimitives = EthPrimitives;
|
||||
pub type OpPrimitives = reth_primitives::EthPrimitives;
|
||||
|
||||
// TODO: once we are ready for separating primitive types, introduce a separate `NodePrimitives`
|
||||
// implementation used exclusively by legacy engine.
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
//! Wrapper of [`OpTypedTransaction`], that implements reth database encoding [`Compact`].
|
||||
|
||||
pub mod signed;
|
||||
pub mod tx_type;
|
||||
|
||||
use alloy_primitives::{bytes, Bytes, TxKind, Uint, B256};
|
||||
|
||||
use alloy_consensus::{constants::EIP7702_TX_TYPE_ID, TxLegacy};
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
use alloy_consensus::constants::EIP7702_TX_TYPE_ID;
|
||||
use alloy_consensus::{SignableTransaction, TxLegacy};
|
||||
use alloy_eips::{eip2930::AccessList, eip7702::SignedAuthorization};
|
||||
use derive_more::{Deref, From};
|
||||
use op_alloy_consensus::{OpTypedTransaction, DEPOSIT_TX_TYPE_ID};
|
||||
use derive_more::{Constructor, Deref, From};
|
||||
use op_alloy_consensus::OpTypedTransaction;
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
use op_alloy_consensus::DEPOSIT_TX_TYPE_ID;
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
use reth_codecs::Compact;
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
use reth_primitives::transaction::{
|
||||
COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930,
|
||||
COMPACT_IDENTIFIER_LEGACY,
|
||||
@ -17,16 +24,31 @@ use reth_primitives_traits::InMemorySize;
|
||||
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deref, Hash, From)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deref, Hash, From, Constructor)]
|
||||
/// Optimistic transaction.
|
||||
pub struct OpTransaction(OpTypedTransaction);
|
||||
|
||||
impl OpTransaction {
|
||||
/// This encodes the transaction _without_ the signature, and is only suitable for creating a
|
||||
/// hash intended for signing.
|
||||
pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
|
||||
match self.deref() {
|
||||
OpTypedTransaction::Legacy(tx) => tx.encode_for_signing(out),
|
||||
OpTypedTransaction::Eip2930(tx) => tx.encode_for_signing(out),
|
||||
OpTypedTransaction::Eip1559(tx) => tx.encode_for_signing(out),
|
||||
OpTypedTransaction::Eip7702(tx) => tx.encode_for_signing(out),
|
||||
OpTypedTransaction::Deposit(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OpTransaction {
|
||||
fn default() -> Self {
|
||||
Self(OpTypedTransaction::Legacy(TxLegacy::default()))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
impl Compact for OpTransaction {
|
||||
fn to_compact<B>(&self, out: &mut B) -> usize
|
||||
where
|
||||
|
||||
479
crates/optimism/primitives/src/transaction/signed.rs
Normal file
479
crates/optimism/primitives/src/transaction/signed.rs
Normal file
@ -0,0 +1,479 @@
|
||||
//! A signed Optimism transaction.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use alloy_consensus::{
|
||||
transaction::RlpEcdsaTx, SignableTransaction, Transaction, TxEip1559, TxEip2930, TxEip7702,
|
||||
};
|
||||
use alloy_eips::{
|
||||
eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718},
|
||||
eip2930::AccessList,
|
||||
eip7702::SignedAuthorization,
|
||||
};
|
||||
use alloy_primitives::{
|
||||
keccak256, Address, Bytes, PrimitiveSignature as Signature, TxHash, TxKind, Uint, B256, U256,
|
||||
};
|
||||
use alloy_rlp::Header;
|
||||
use derive_more::{AsRef, Deref};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use once_cell::sync::OnceCell as OnceLock;
|
||||
use op_alloy_consensus::{OpTypedTransaction, TxDeposit};
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
use proptest as _;
|
||||
use reth_primitives::{
|
||||
transaction::{recover_signer, recover_signer_unchecked},
|
||||
TransactionSigned,
|
||||
};
|
||||
use reth_primitives_traits::{FillTxEnv, InMemorySize, SignedTransaction};
|
||||
use revm_primitives::{AuthorizationList, OptimismFields, TxEnv};
|
||||
|
||||
use crate::{OpTransaction, OpTxType};
|
||||
|
||||
/// Signed transaction.
|
||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(rlp))]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[derive(Debug, Clone, Eq, AsRef, Deref)]
|
||||
pub struct OpTransactionSigned {
|
||||
/// Transaction hash
|
||||
#[serde(skip)]
|
||||
pub hash: OnceLock<TxHash>,
|
||||
/// The transaction signature values
|
||||
pub signature: Signature,
|
||||
/// Raw transaction info
|
||||
#[deref]
|
||||
#[as_ref]
|
||||
pub transaction: OpTransaction,
|
||||
}
|
||||
|
||||
impl OpTransactionSigned {
|
||||
/// Calculates hash of given transaction and signature and returns new instance.
|
||||
pub fn new(transaction: OpTypedTransaction, signature: Signature) -> Self {
|
||||
let signed_tx = Self::new_unhashed(transaction, signature);
|
||||
if !matches!(signed_tx.tx_type(), OpTxType::Deposit) {
|
||||
signed_tx.hash.get_or_init(|| signed_tx.recalculate_hash());
|
||||
}
|
||||
|
||||
signed_tx
|
||||
}
|
||||
|
||||
/// Creates a new signed transaction from the given transaction and signature without the hash.
|
||||
///
|
||||
/// Note: this only calculates the hash on the first [`TransactionSigned::hash`] call.
|
||||
pub fn new_unhashed(transaction: OpTypedTransaction, signature: Signature) -> Self {
|
||||
Self { hash: Default::default(), signature, transaction: OpTransaction::new(transaction) }
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedTransaction for OpTransactionSigned {
|
||||
type Type = OpTxType;
|
||||
|
||||
fn tx_hash(&self) -> &TxHash {
|
||||
self.hash.get_or_init(|| self.recalculate_hash())
|
||||
}
|
||||
|
||||
fn signature(&self) -> &Signature {
|
||||
&self.signature
|
||||
}
|
||||
|
||||
fn recover_signer(&self) -> Option<Address> {
|
||||
// Optimism's Deposit transaction does not have a signature. Directly return the
|
||||
// `from` address.
|
||||
if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = *self.transaction {
|
||||
return Some(from)
|
||||
}
|
||||
|
||||
let Self { transaction, signature, .. } = self;
|
||||
let signature_hash = signature_hash(transaction);
|
||||
recover_signer(signature, signature_hash)
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked(&self) -> Option<Address> {
|
||||
// Optimism's Deposit transaction does not have a signature. Directly return the
|
||||
// `from` address.
|
||||
if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = *self.transaction {
|
||||
return Some(from)
|
||||
}
|
||||
|
||||
let Self { transaction, signature, .. } = self;
|
||||
let signature_hash = signature_hash(transaction);
|
||||
recover_signer_unchecked(signature, signature_hash)
|
||||
}
|
||||
|
||||
fn recalculate_hash(&self) -> B256 {
|
||||
keccak256(self.encoded_2718())
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
|
||||
// Optimism's Deposit transaction does not have a signature. Directly return the
|
||||
// `from` address.
|
||||
if let OpTypedTransaction::Deposit(TxDeposit { from, .. }) = *self.transaction {
|
||||
return Some(from)
|
||||
}
|
||||
self.encode_for_signing(buf);
|
||||
let signature_hash = keccak256(buf);
|
||||
recover_signer_unchecked(&self.signature, signature_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl FillTxEnv for OpTransactionSigned {
|
||||
fn fill_tx_env(&self, tx_env: &mut TxEnv, sender: Address) {
|
||||
let envelope = self.encoded_2718();
|
||||
|
||||
tx_env.caller = sender;
|
||||
match self.transaction.deref() {
|
||||
OpTypedTransaction::Legacy(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.gas_price);
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to = tx.to;
|
||||
tx_env.value = tx.value;
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = tx.chain_id;
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list.clear();
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
tx_env.authorization_list = None;
|
||||
}
|
||||
OpTypedTransaction::Eip2930(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.gas_price);
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to = tx.to;
|
||||
tx_env.value = tx.value;
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list.clone_from(&tx.access_list.0);
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
tx_env.authorization_list = None;
|
||||
}
|
||||
OpTypedTransaction::Eip1559(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
|
||||
tx_env.transact_to = tx.to;
|
||||
tx_env.value = tx.value;
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list.clone_from(&tx.access_list.0);
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
tx_env.authorization_list = None;
|
||||
}
|
||||
OpTypedTransaction::Eip7702(tx) => {
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::from(tx.max_fee_per_gas);
|
||||
tx_env.gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas));
|
||||
tx_env.transact_to = tx.to.into();
|
||||
tx_env.value = tx.value;
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = Some(tx.chain_id);
|
||||
tx_env.nonce = Some(tx.nonce);
|
||||
tx_env.access_list.clone_from(&tx.access_list.0);
|
||||
tx_env.blob_hashes.clear();
|
||||
tx_env.max_fee_per_blob_gas.take();
|
||||
tx_env.authorization_list =
|
||||
Some(AuthorizationList::Signed(tx.authorization_list.clone()));
|
||||
}
|
||||
OpTypedTransaction::Deposit(tx) => {
|
||||
tx_env.access_list.clear();
|
||||
tx_env.gas_limit = tx.gas_limit;
|
||||
tx_env.gas_price = U256::ZERO;
|
||||
tx_env.gas_priority_fee = None;
|
||||
tx_env.transact_to = tx.to;
|
||||
tx_env.value = tx.value;
|
||||
tx_env.data = tx.input.clone();
|
||||
tx_env.chain_id = None;
|
||||
tx_env.nonce = None;
|
||||
tx_env.authorization_list = None;
|
||||
|
||||
tx_env.optimism = OptimismFields {
|
||||
source_hash: Some(tx.source_hash),
|
||||
mint: tx.mint,
|
||||
is_system_transaction: Some(tx.is_system_transaction),
|
||||
enveloped_tx: Some(envelope.into()),
|
||||
};
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tx_env.optimism = OptimismFields {
|
||||
source_hash: None,
|
||||
mint: None,
|
||||
is_system_transaction: Some(false),
|
||||
enveloped_tx: Some(envelope.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for OpTransactionSigned {
|
||||
#[inline]
|
||||
fn size(&self) -> usize {
|
||||
mem::size_of::<TxHash>() + self.transaction.size() + mem::size_of::<Signature>()
|
||||
}
|
||||
}
|
||||
|
||||
impl alloy_rlp::Encodable for OpTransactionSigned {
|
||||
/// See [`alloy_rlp::Encodable`] impl for [`TransactionSigned`].
|
||||
fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) {
|
||||
self.network_encode(out);
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
let mut payload_length = self.encode_2718_len();
|
||||
if !self.is_legacy() {
|
||||
payload_length += Header { list: false, payload_length }.length();
|
||||
}
|
||||
|
||||
payload_length
|
||||
}
|
||||
}
|
||||
|
||||
impl alloy_rlp::Decodable for OpTransactionSigned {
|
||||
/// See [`alloy_rlp::Decodable`] impl for [`TransactionSigned`].
|
||||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
Self::network_decode(buf).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable2718 for OpTransactionSigned {
|
||||
fn type_flag(&self) -> Option<u8> {
|
||||
match self.tx_type() {
|
||||
op_alloy_consensus::OpTxType::Legacy => None,
|
||||
tx_type => Some(tx_type as u8),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_2718_len(&self) -> usize {
|
||||
match self.transaction.deref() {
|
||||
OpTypedTransaction::Legacy(legacy_tx) => {
|
||||
legacy_tx.eip2718_encoded_length(&self.signature)
|
||||
}
|
||||
OpTypedTransaction::Eip2930(access_list_tx) => {
|
||||
access_list_tx.eip2718_encoded_length(&self.signature)
|
||||
}
|
||||
OpTypedTransaction::Eip1559(dynamic_fee_tx) => {
|
||||
dynamic_fee_tx.eip2718_encoded_length(&self.signature)
|
||||
}
|
||||
OpTypedTransaction::Eip7702(set_code_tx) => {
|
||||
set_code_tx.eip2718_encoded_length(&self.signature)
|
||||
}
|
||||
OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.eip2718_encoded_length(),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
|
||||
let Self { transaction, signature, .. } = self;
|
||||
|
||||
match transaction.deref() {
|
||||
OpTypedTransaction::Legacy(legacy_tx) => {
|
||||
// do nothing w/ with_header
|
||||
legacy_tx.eip2718_encode(signature, out)
|
||||
}
|
||||
OpTypedTransaction::Eip2930(access_list_tx) => {
|
||||
access_list_tx.eip2718_encode(signature, out)
|
||||
}
|
||||
OpTypedTransaction::Eip1559(dynamic_fee_tx) => {
|
||||
dynamic_fee_tx.eip2718_encode(signature, out)
|
||||
}
|
||||
OpTypedTransaction::Eip7702(set_code_tx) => set_code_tx.eip2718_encode(signature, out),
|
||||
OpTypedTransaction::Deposit(deposit_tx) => deposit_tx.encode_2718(out),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable2718 for OpTransactionSigned {
|
||||
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
|
||||
match ty.try_into().map_err(|_| Eip2718Error::UnexpectedType(ty))? {
|
||||
op_alloy_consensus::OpTxType::Legacy => Err(Eip2718Error::UnexpectedType(0)),
|
||||
op_alloy_consensus::OpTxType::Eip2930 => {
|
||||
let (tx, signature, hash) = TxEip2930::rlp_decode_signed(buf)?.into_parts();
|
||||
let signed_tx = Self::new_unhashed(OpTypedTransaction::Eip2930(tx), signature);
|
||||
signed_tx.hash.get_or_init(|| hash);
|
||||
Ok(signed_tx)
|
||||
}
|
||||
op_alloy_consensus::OpTxType::Eip1559 => {
|
||||
let (tx, signature, hash) = TxEip1559::rlp_decode_signed(buf)?.into_parts();
|
||||
let signed_tx = Self::new_unhashed(OpTypedTransaction::Eip1559(tx), signature);
|
||||
signed_tx.hash.get_or_init(|| hash);
|
||||
Ok(signed_tx)
|
||||
}
|
||||
op_alloy_consensus::OpTxType::Eip7702 => {
|
||||
let (tx, signature, hash) = TxEip7702::rlp_decode_signed(buf)?.into_parts();
|
||||
let signed_tx = Self::new_unhashed(OpTypedTransaction::Eip7702(tx), signature);
|
||||
signed_tx.hash.get_or_init(|| hash);
|
||||
Ok(signed_tx)
|
||||
}
|
||||
op_alloy_consensus::OpTxType::Deposit => Ok(Self::new_unhashed(
|
||||
OpTypedTransaction::Deposit(TxDeposit::rlp_decode(buf)?),
|
||||
TxDeposit::signature(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
|
||||
let (transaction, hash, signature) =
|
||||
TransactionSigned::decode_rlp_legacy_transaction_tuple(buf)?;
|
||||
let signed_tx = Self::new_unhashed(OpTypedTransaction::Legacy(transaction), signature);
|
||||
signed_tx.hash.get_or_init(|| hash);
|
||||
|
||||
Ok(signed_tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl Transaction for OpTransactionSigned {
|
||||
fn chain_id(&self) -> Option<u64> {
|
||||
self.deref().chain_id()
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
self.deref().nonce()
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
self.deref().gas_limit()
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> Option<u128> {
|
||||
self.deref().gas_price()
|
||||
}
|
||||
|
||||
fn max_fee_per_gas(&self) -> u128 {
|
||||
self.deref().max_fee_per_gas()
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
self.deref().max_priority_fee_per_gas()
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> Option<u128> {
|
||||
self.deref().max_fee_per_blob_gas()
|
||||
}
|
||||
|
||||
fn priority_fee_or_price(&self) -> u128 {
|
||||
self.deref().priority_fee_or_price()
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
self.deref().kind()
|
||||
}
|
||||
|
||||
fn is_create(&self) -> bool {
|
||||
self.deref().is_create()
|
||||
}
|
||||
|
||||
fn value(&self) -> Uint<256, 4> {
|
||||
self.deref().value()
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
self.deref().input()
|
||||
}
|
||||
|
||||
fn ty(&self) -> u8 {
|
||||
self.deref().ty()
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<&AccessList> {
|
||||
self.deref().access_list()
|
||||
}
|
||||
|
||||
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
|
||||
self.deref().blob_versioned_hashes()
|
||||
}
|
||||
|
||||
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
|
||||
self.deref().authorization_list()
|
||||
}
|
||||
|
||||
fn is_dynamic_fee(&self) -> bool {
|
||||
self.deref().is_dynamic_fee()
|
||||
}
|
||||
|
||||
fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
|
||||
self.deref().effective_gas_price(base_fee)
|
||||
}
|
||||
|
||||
fn effective_tip_per_gas(&self, base_fee: u64) -> Option<u128> {
|
||||
self.deref().effective_tip_per_gas(base_fee)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for OpTransactionSigned {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
hash: Default::default(),
|
||||
signature: Signature::test_signature(),
|
||||
transaction: OpTransaction::new(OpTypedTransaction::Legacy(Default::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for OpTransactionSigned {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.signature == other.signature &&
|
||||
self.transaction == other.transaction &&
|
||||
self.tx_hash() == other.tx_hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for OpTransactionSigned {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.signature.hash(state);
|
||||
self.transaction.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for OpTransactionSigned {
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
#[allow(unused_mut)]
|
||||
let mut transaction = OpTypedTransaction::arbitrary(u)?;
|
||||
|
||||
let secp = secp256k1::Secp256k1::new();
|
||||
let key_pair = secp256k1::Keypair::new(&secp, &mut rand::thread_rng());
|
||||
let signature = reth_primitives::transaction::util::secp256k1::sign_message(
|
||||
B256::from_slice(&key_pair.secret_bytes()[..]),
|
||||
signature_hash(&transaction),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Both `Some(0)` and `None` values are encoded as empty string byte. This introduces
|
||||
// ambiguity in roundtrip tests. Patch the mint value of deposit transaction here, so that
|
||||
// it's `None` if zero.
|
||||
if let OpTypedTransaction::Deposit(ref mut tx_deposit) = transaction {
|
||||
if tx_deposit.mint == Some(0) {
|
||||
tx_deposit.mint = None;
|
||||
}
|
||||
}
|
||||
|
||||
let signature = if is_deposit(&transaction) { TxDeposit::signature() } else { signature };
|
||||
|
||||
Ok(Self::new(transaction, signature))
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the signing hash for the transaction.
|
||||
pub fn signature_hash(tx: &OpTypedTransaction) -> B256 {
|
||||
match tx {
|
||||
OpTypedTransaction::Legacy(tx) => tx.signature_hash(),
|
||||
OpTypedTransaction::Eip2930(tx) => tx.signature_hash(),
|
||||
OpTypedTransaction::Eip1559(tx) => tx.signature_hash(),
|
||||
OpTypedTransaction::Eip7702(tx) => tx.signature_hash(),
|
||||
OpTypedTransaction::Deposit(_) => B256::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if transaction is deposit transaction.
|
||||
pub const fn is_deposit(tx: &OpTypedTransaction) -> bool {
|
||||
matches!(tx, OpTypedTransaction::Deposit(_))
|
||||
}
|
||||
@ -72,5 +72,6 @@ optimism = [
|
||||
"reth-provider/optimism",
|
||||
"revm/optimism",
|
||||
"reth-optimism-consensus/optimism",
|
||||
"reth-optimism-payload-builder/optimism"
|
||||
"reth-optimism-payload-builder/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
]
|
||||
|
||||
@ -42,6 +42,11 @@ pub use sidecar::BlobTransaction;
|
||||
pub use signature::{recover_signer, recover_signer_unchecked};
|
||||
pub use tx_type::TxType;
|
||||
|
||||
/// Handling transaction signature operations, including signature recovery,
|
||||
/// applying chain IDs, and EIP-2 validation.
|
||||
pub mod signature;
|
||||
pub mod util;
|
||||
|
||||
pub(crate) mod access_list;
|
||||
mod compat;
|
||||
mod error;
|
||||
@ -50,12 +55,6 @@ mod pooled;
|
||||
mod sidecar;
|
||||
mod tx_type;
|
||||
|
||||
/// Handling transaction signature operations, including signature recovery,
|
||||
/// applying chain IDs, and EIP-2 validation.
|
||||
pub mod signature;
|
||||
|
||||
pub(crate) mod util;
|
||||
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
pub use tx_type::{
|
||||
COMPACT_EXTENDED_IDENTIFIER_FLAG, COMPACT_IDENTIFIER_EIP1559, COMPACT_IDENTIFIER_EIP2930,
|
||||
@ -1178,7 +1177,7 @@ impl TransactionSigned {
|
||||
///
|
||||
/// Refer to the docs for [`Self::decode_rlp_legacy_transaction`] for details on the exact
|
||||
/// format expected.
|
||||
pub(crate) fn decode_rlp_legacy_transaction_tuple(
|
||||
pub fn decode_rlp_legacy_transaction_tuple(
|
||||
data: &mut &[u8],
|
||||
) -> alloy_rlp::Result<(TxLegacy, TxHash, Signature)> {
|
||||
// keep this around, so we can use it to calculate the hash
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
//! Utility functions for signature.
|
||||
|
||||
use alloy_primitives::{Address, PrimitiveSignature as Signature};
|
||||
|
||||
/// Secp256k1 utility functions.
|
||||
#[cfg(feature = "secp256k1")]
|
||||
pub(crate) mod secp256k1 {
|
||||
pub mod secp256k1 {
|
||||
pub use super::impl_secp256k1::*;
|
||||
}
|
||||
|
||||
|
||||
@ -96,6 +96,7 @@ optimism = [
|
||||
"reth-db/optimism",
|
||||
"reth-db-api/optimism",
|
||||
"revm/optimism",
|
||||
"reth-optimism-primitives/op",
|
||||
]
|
||||
serde = [
|
||||
"dashmap/serde",
|
||||
|
||||
Reference in New Issue
Block a user