mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: migrate to alloy TxLegacy (#9593)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
This commit is contained in:
@ -364,7 +364,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x8u64,
|
nonce: 0x8u64,
|
||||||
gas_price: 0x4a817c808,
|
gas_price: 0x4a817c808,
|
||||||
gas_limit: 0x2e248u64,
|
gas_limit: 0x2e248,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x200u64),
|
value: U256::from(0x200u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -379,7 +379,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x9u64,
|
nonce: 0x9u64,
|
||||||
gas_price: 0x4a817c809,
|
gas_price: 0x4a817c809,
|
||||||
gas_limit: 0x33450u64,
|
gas_limit: 0x33450,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x2d9u64),
|
value: U256::from(0x2d9u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -438,7 +438,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x8u64,
|
nonce: 0x8u64,
|
||||||
gas_price: 0x4a817c808,
|
gas_price: 0x4a817c808,
|
||||||
gas_limit: 0x2e248u64,
|
gas_limit: 0x2e248,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x200u64),
|
value: U256::from(0x200u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -454,7 +454,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x9u64,
|
nonce: 0x9u64,
|
||||||
gas_price: 0x4a817c809,
|
gas_price: 0x4a817c809,
|
||||||
gas_limit: 0x33450u64,
|
gas_limit: 0x33450,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x2d9u64),
|
value: U256::from(0x2d9u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
|
|||||||
@ -127,7 +127,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x8u64,
|
nonce: 0x8u64,
|
||||||
gas_price: 0x4a817c808,
|
gas_price: 0x4a817c808,
|
||||||
gas_limit: 0x2e248u64,
|
gas_limit: 0x2e248,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x200u64),
|
value: U256::from(0x200u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -149,7 +149,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x09u64,
|
nonce: 0x09u64,
|
||||||
gas_price: 0x4a817c809,
|
gas_price: 0x4a817c809,
|
||||||
gas_limit: 0x33450u64,
|
gas_limit: 0x33450,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x2d9u64),
|
value: U256::from(0x2d9u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -193,7 +193,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x8u64,
|
nonce: 0x8u64,
|
||||||
gas_price: 0x4a817c808,
|
gas_price: 0x4a817c808,
|
||||||
gas_limit: 0x2e248u64,
|
gas_limit: 0x2e248,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x200u64),
|
value: U256::from(0x200u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -215,7 +215,7 @@ mod tests {
|
|||||||
chain_id: Some(1),
|
chain_id: Some(1),
|
||||||
nonce: 0x09u64,
|
nonce: 0x09u64,
|
||||||
gas_price: 0x4a817c809,
|
gas_price: 0x4a817c809,
|
||||||
gas_limit: 0x33450u64,
|
gas_limit: 0x33450,
|
||||||
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
|
||||||
value: U256::from(0x2d9u64),
|
value: U256::from(0x2d9u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -260,7 +260,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 15u64,
|
nonce: 15u64,
|
||||||
gas_price: 2200000000,
|
gas_price: 2200000000,
|
||||||
gas_limit: 34811u64,
|
gas_limit: 34811,
|
||||||
to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
|
to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
|
||||||
value: U256::from(1234u64),
|
value: U256::from(1234u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -306,7 +306,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 3u64,
|
nonce: 3u64,
|
||||||
gas_price: 2000000000,
|
gas_price: 2000000000,
|
||||||
gas_limit: 10000000u64,
|
gas_limit: 10000000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(1000000000000000u64),
|
value: U256::from(1000000000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -328,7 +328,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 1u64,
|
nonce: 1u64,
|
||||||
gas_price: 1000000000,
|
gas_price: 1000000000,
|
||||||
gas_limit: 100000u64,
|
gas_limit: 100000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(693361000000000u64),
|
value: U256::from(693361000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -350,7 +350,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 2u64,
|
nonce: 2u64,
|
||||||
gas_price: 1000000000,
|
gas_price: 1000000000,
|
||||||
gas_limit: 100000u64,
|
gas_limit: 100000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(1000000000000000u64),
|
value: U256::from(1000000000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -399,7 +399,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 15u64,
|
nonce: 15u64,
|
||||||
gas_price: 2200000000,
|
gas_price: 2200000000,
|
||||||
gas_limit: 34811u64,
|
gas_limit: 34811,
|
||||||
to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
|
to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
|
||||||
value: U256::from(1234u64),
|
value: U256::from(1234u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -445,7 +445,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 3u64,
|
nonce: 3u64,
|
||||||
gas_price: 2000000000,
|
gas_price: 2000000000,
|
||||||
gas_limit: 10000000u64,
|
gas_limit: 10000000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(1000000000000000u64),
|
value: U256::from(1000000000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -467,7 +467,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 1u64,
|
nonce: 1u64,
|
||||||
gas_price: 1000000000,
|
gas_price: 1000000000,
|
||||||
gas_limit: 100000u64,
|
gas_limit: 100000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(693361000000000u64),
|
value: U256::from(693361000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
@ -489,7 +489,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 2u64,
|
nonce: 2u64,
|
||||||
gas_price: 1000000000,
|
gas_price: 1000000000,
|
||||||
gas_limit: 100000u64,
|
gas_limit: 100000,
|
||||||
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
|
||||||
value: U256::from(1000000000000000u64),
|
value: U256::from(1000000000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
|
|||||||
@ -23,13 +23,13 @@ reth-codecs = { workspace = true, optional = true }
|
|||||||
reth-optimism-chainspec = { workspace = true, optional = true }
|
reth-optimism-chainspec = { workspace = true, optional = true }
|
||||||
|
|
||||||
# ethereum
|
# ethereum
|
||||||
|
alloy-consensus.workspace = true
|
||||||
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
|
alloy-primitives = { workspace = true, features = ["rand", "rlp"] }
|
||||||
alloy-rlp = { workspace = true, features = ["arrayvec"] }
|
alloy-rlp = { workspace = true, features = ["arrayvec"] }
|
||||||
alloy-rpc-types = { workspace = true, optional = true }
|
alloy-rpc-types = { workspace = true, optional = true }
|
||||||
alloy-serde = { workspace = true, optional = true }
|
alloy-serde = { workspace = true, optional = true }
|
||||||
alloy-genesis.workspace = true
|
alloy-genesis.workspace = true
|
||||||
alloy-eips = { workspace = true, features = ["serde"] }
|
alloy-eips = { workspace = true, features = ["serde"] }
|
||||||
alloy-consensus.workspace = true
|
|
||||||
|
|
||||||
# optimism
|
# optimism
|
||||||
op-alloy-rpc-types = { workspace = true, optional = true }
|
op-alloy-rpc-types = { workspace = true, optional = true }
|
||||||
@ -118,4 +118,3 @@ harness = false
|
|||||||
name = "validate_blob_tx"
|
name = "validate_blob_tx"
|
||||||
required-features = ["arbitrary", "c-kzg"]
|
required-features = ["arbitrary", "c-kzg"]
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
|||||||
@ -102,10 +102,7 @@ impl TryFrom<WithOtherFields<alloy_rpc_types::Transaction>> for Transaction {
|
|||||||
chain_id,
|
chain_id,
|
||||||
nonce: tx.nonce,
|
nonce: tx.nonce,
|
||||||
gas_price: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?,
|
gas_price: tx.gas_price.ok_or(ConversionError::MissingGasPrice)?,
|
||||||
gas_limit: tx
|
gas_limit: tx.gas,
|
||||||
.gas
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| ConversionError::Eip2718Error(RlpError::Overflow.into()))?,
|
|
||||||
to: tx.to.map_or(TxKind::Create, TxKind::Call),
|
to: tx.to.map_or(TxKind::Create, TxKind::Call),
|
||||||
value: tx.value,
|
value: tx.value,
|
||||||
input: tx.input,
|
input: tx.input,
|
||||||
|
|||||||
@ -22,7 +22,7 @@ impl FillTxEnv for TransactionSigned {
|
|||||||
tx_env.caller = sender;
|
tx_env.caller = sender;
|
||||||
match self.as_ref() {
|
match self.as_ref() {
|
||||||
Transaction::Legacy(tx) => {
|
Transaction::Legacy(tx) => {
|
||||||
tx_env.gas_limit = tx.gas_limit;
|
tx_env.gas_limit = tx.gas_limit as u64;
|
||||||
tx_env.gas_price = U256::from(tx.gas_price);
|
tx_env.gas_price = U256::from(tx.gas_price);
|
||||||
tx_env.gas_priority_fee = None;
|
tx_env.gas_priority_fee = None;
|
||||||
tx_env.transact_to = tx.to;
|
tx_env.transact_to = tx.to;
|
||||||
|
|||||||
@ -1,180 +1,4 @@
|
|||||||
use crate::{keccak256, Bytes, ChainId, Signature, TxKind, TxType, B256, U256};
|
pub use alloy_consensus::TxLegacy;
|
||||||
use alloy_rlp::{length_of_length, Encodable, Header};
|
|
||||||
use core::mem;
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "reth-codec"))]
|
|
||||||
use reth_codecs::Compact;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
/// Legacy transaction.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
|
||||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
|
|
||||||
#[cfg_attr(any(test, feature = "reth-codec"), derive(Compact))]
|
|
||||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
|
|
||||||
pub struct TxLegacy {
|
|
||||||
/// Added as EIP-155: Simple replay attack protection
|
|
||||||
pub chain_id: Option<ChainId>,
|
|
||||||
/// A scalar value equal to the number of transactions sent by the sender; formally Tn.
|
|
||||||
pub nonce: u64,
|
|
||||||
/// A scalar value equal to the number of
|
|
||||||
/// Wei to be paid per unit of gas for all computation
|
|
||||||
/// costs incurred as a result of the execution of this transaction; formally Tp.
|
|
||||||
///
|
|
||||||
/// As ethereum circulation is around 120mil eth as of 2022 that is around
|
|
||||||
/// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
|
|
||||||
/// 340282366920938463463374607431768211455
|
|
||||||
pub gas_price: u128,
|
|
||||||
/// A scalar value equal to the maximum
|
|
||||||
/// amount of gas that should be used in executing
|
|
||||||
/// this transaction. This is paid up-front, before any
|
|
||||||
/// computation is done and may not be increased
|
|
||||||
/// later; formally Tg.
|
|
||||||
pub gas_limit: u64,
|
|
||||||
/// The 160-bit address of the message call’s recipient or, for a contract creation
|
|
||||||
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
|
|
||||||
pub to: TxKind,
|
|
||||||
/// A scalar value equal to the number of Wei to
|
|
||||||
/// be transferred to the message call’s recipient or,
|
|
||||||
/// in the case of contract creation, as an endowment
|
|
||||||
/// to the newly created account; formally Tv.
|
|
||||||
pub value: U256,
|
|
||||||
/// Input has two uses depending if transaction is Create or Call (if `to` field is None or
|
|
||||||
/// Some). pub init: An unlimited size byte array specifying the
|
|
||||||
/// EVM-code for the account initialisation procedure CREATE,
|
|
||||||
/// data: An unlimited size byte array specifying the
|
|
||||||
/// input data of the message call, formally Td.
|
|
||||||
pub input: Bytes,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TxLegacy {
|
|
||||||
/// Calculates a heuristic for the in-memory size of the [`TxLegacy`] transaction.
|
|
||||||
#[inline]
|
|
||||||
pub fn size(&self) -> usize {
|
|
||||||
mem::size_of::<Option<ChainId>>() + // chain_id
|
|
||||||
mem::size_of::<u64>() + // nonce
|
|
||||||
mem::size_of::<u128>() + // gas_price
|
|
||||||
mem::size_of::<u64>() + // gas_limit
|
|
||||||
self.to.size() + // to
|
|
||||||
mem::size_of::<U256>() + // value
|
|
||||||
self.input.len() // input
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the length of the transaction's fields, without a RLP header or length of the
|
|
||||||
/// eip155 fields.
|
|
||||||
pub(crate) fn fields_len(&self) -> usize {
|
|
||||||
self.nonce.length() +
|
|
||||||
self.gas_price.length() +
|
|
||||||
self.gas_limit.length() +
|
|
||||||
self.to.length() +
|
|
||||||
self.value.length() +
|
|
||||||
self.input.0.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes only the transaction's fields into the desired buffer, without a RLP header or
|
|
||||||
/// eip155 fields.
|
|
||||||
pub(crate) fn encode_fields(&self, out: &mut dyn bytes::BufMut) {
|
|
||||||
self.nonce.encode(out);
|
|
||||||
self.gas_price.encode(out);
|
|
||||||
self.gas_limit.encode(out);
|
|
||||||
self.to.encode(out);
|
|
||||||
self.value.encode(out);
|
|
||||||
self.input.0.encode(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Inner encoding function that is used for both rlp [`Encodable`] trait and for calculating
|
|
||||||
/// hash.
|
|
||||||
///
|
|
||||||
/// This encodes the transaction as:
|
|
||||||
/// `rlp(nonce, gas_price, gas_limit, to, value, input, v, r, s)`
|
|
||||||
///
|
|
||||||
/// The `v` value is encoded according to EIP-155 if the `chain_id` is not `None`.
|
|
||||||
pub(crate) fn encode_with_signature(&self, signature: &Signature, out: &mut dyn bytes::BufMut) {
|
|
||||||
let payload_length =
|
|
||||||
self.fields_len() + signature.payload_len_with_eip155_chain_id(self.chain_id);
|
|
||||||
let header = Header { list: true, payload_length };
|
|
||||||
header.encode(out);
|
|
||||||
self.encode_fields(out);
|
|
||||||
signature.encode_with_eip155_chain_id(out, self.chain_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Output the length of the RLP signed transaction encoding.
|
|
||||||
pub(crate) fn payload_len_with_signature(&self, signature: &Signature) -> usize {
|
|
||||||
let payload_length =
|
|
||||||
self.fields_len() + signature.payload_len_with_eip155_chain_id(self.chain_id);
|
|
||||||
// 'header length' + 'payload length'
|
|
||||||
length_of_length(payload_length) + payload_length
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get transaction type
|
|
||||||
pub(crate) const fn tx_type(&self) -> TxType {
|
|
||||||
TxType::Legacy
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes EIP-155 arguments into the desired buffer. Only encodes values for legacy
|
|
||||||
/// transactions.
|
|
||||||
///
|
|
||||||
/// If a `chain_id` is `Some`, this encodes the `chain_id`, followed by two zeroes, as defined
|
|
||||||
/// by [EIP-155](https://eips.ethereum.org/EIPS/eip-155).
|
|
||||||
pub(crate) fn encode_eip155_fields(&self, out: &mut dyn bytes::BufMut) {
|
|
||||||
// if this is a legacy transaction without a chain ID, it must be pre-EIP-155
|
|
||||||
// and does not need to encode the chain ID for the signature hash encoding
|
|
||||||
if let Some(id) = self.chain_id {
|
|
||||||
// EIP-155 encodes the chain ID and two zeroes
|
|
||||||
id.encode(out);
|
|
||||||
0x00u8.encode(out);
|
|
||||||
0x00u8.encode(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the length of EIP-155 fields. Only outputs a non-zero value for EIP-155 legacy
|
|
||||||
/// transactions.
|
|
||||||
pub(crate) fn eip155_fields_len(&self) -> usize {
|
|
||||||
if let Some(id) = self.chain_id {
|
|
||||||
// EIP-155 encodes the chain ID and two zeroes, so we add 2 to the length of the chain
|
|
||||||
// ID to get the length of all 3 fields
|
|
||||||
// len(chain_id) + (0x00) + (0x00)
|
|
||||||
id.length() + 2
|
|
||||||
} else {
|
|
||||||
// this is either a pre-EIP-155 legacy transaction or a typed transaction
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encodes the legacy transaction in RLP for signing, including the EIP-155 fields if possible.
|
|
||||||
///
|
|
||||||
/// If a `chain_id` is `Some`, this encodes the transaction as:
|
|
||||||
/// `rlp(nonce, gas_price, gas_limit, to, value, input, chain_id, 0, 0)`
|
|
||||||
///
|
|
||||||
/// Otherwise, this encodes the transaction as:
|
|
||||||
/// `rlp(nonce, gas_price, gas_limit, to, value, input)`
|
|
||||||
pub(crate) fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
|
|
||||||
Header { list: true, payload_length: self.fields_len() + self.eip155_fields_len() }
|
|
||||||
.encode(out);
|
|
||||||
self.encode_fields(out);
|
|
||||||
self.encode_eip155_fields(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the length of the signature RLP encoding for the transaction, including the length
|
|
||||||
/// of the EIP-155 fields if possible.
|
|
||||||
pub(crate) fn payload_len_for_signature(&self) -> usize {
|
|
||||||
let payload_length = self.fields_len() + self.eip155_fields_len();
|
|
||||||
// 'header length' + 'payload length'
|
|
||||||
length_of_length(payload_length) + payload_length
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Outputs the signature hash of the transaction by first encoding without a signature, then
|
|
||||||
/// hashing.
|
|
||||||
///
|
|
||||||
/// See [`Self::encode_for_signing`] for more information on the encoding format.
|
|
||||||
pub(crate) fn signature_hash(&self) -> B256 {
|
|
||||||
let mut buf = Vec::with_capacity(self.payload_len_for_signature());
|
|
||||||
self.encode_for_signing(&mut buf);
|
|
||||||
keccak256(&buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use crate::{
|
|||||||
B256, U256,
|
B256, U256,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use alloy_consensus::SignableTransaction;
|
||||||
use alloy_rlp::{
|
use alloy_rlp::{
|
||||||
Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE,
|
Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE, EMPTY_STRING_CODE,
|
||||||
};
|
};
|
||||||
@ -87,7 +88,6 @@ pub(crate) static PARALLEL_SENDER_RECOVERY_THRESHOLD: Lazy<usize> =
|
|||||||
///
|
///
|
||||||
/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718).
|
/// Transaction types were introduced in [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718).
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
|
|
||||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
|
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
|
||||||
pub enum Transaction {
|
pub enum Transaction {
|
||||||
/// Legacy transaction (type `0x0`).
|
/// Legacy transaction (type `0x0`).
|
||||||
@ -140,6 +140,27 @@ pub enum Transaction {
|
|||||||
Deposit(TxDeposit),
|
Deposit(TxDeposit),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "arbitrary"))]
|
||||||
|
impl<'a> arbitrary::Arbitrary<'a> for Transaction {
|
||||||
|
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||||
|
let mut tx = match TxType::arbitrary(u)? {
|
||||||
|
TxType::Legacy => Self::Legacy(TxLegacy::arbitrary(u)?),
|
||||||
|
TxType::Eip2930 => Self::Eip2930(TxEip2930::arbitrary(u)?),
|
||||||
|
TxType::Eip1559 => Self::Eip1559(TxEip1559::arbitrary(u)?),
|
||||||
|
TxType::Eip4844 => Self::Eip4844(TxEip4844::arbitrary(u)?),
|
||||||
|
TxType::Eip7702 => Self::Eip7702(TxEip7702::arbitrary(u)?),
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
TxType::Deposit => Self::Deposit(TxDeposit::arbitrary(u)?),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Self::Legacy(tx) = &mut tx {
|
||||||
|
tx.gas_limit = (tx.gas_limit as u64).into();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === impl Transaction ===
|
// === impl Transaction ===
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
@ -208,7 +229,7 @@ impl Transaction {
|
|||||||
/// Get the transaction's type
|
/// Get the transaction's type
|
||||||
pub const fn tx_type(&self) -> TxType {
|
pub const fn tx_type(&self) -> TxType {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(legacy_tx) => legacy_tx.tx_type(),
|
Self::Legacy(_) => TxType::Legacy,
|
||||||
Self::Eip2930(access_list_tx) => access_list_tx.tx_type(),
|
Self::Eip2930(access_list_tx) => access_list_tx.tx_type(),
|
||||||
Self::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.tx_type(),
|
Self::Eip1559(dynamic_fee_tx) => dynamic_fee_tx.tx_type(),
|
||||||
Self::Eip4844(blob_tx) => blob_tx.tx_type(),
|
Self::Eip4844(blob_tx) => blob_tx.tx_type(),
|
||||||
@ -273,7 +294,7 @@ impl Transaction {
|
|||||||
/// Get the gas limit of the transaction.
|
/// Get the gas limit of the transaction.
|
||||||
pub const fn gas_limit(&self) -> u64 {
|
pub const fn gas_limit(&self) -> u64 {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(TxLegacy { gas_limit, .. }) |
|
Self::Legacy(TxLegacy { gas_limit, .. }) => *gas_limit as u64,
|
||||||
Self::Eip2930(TxEip2930 { gas_limit, .. }) |
|
Self::Eip2930(TxEip2930 { gas_limit, .. }) |
|
||||||
Self::Eip1559(TxEip1559 { gas_limit, .. }) |
|
Self::Eip1559(TxEip1559 { gas_limit, .. }) |
|
||||||
Self::Eip4844(TxEip4844 { gas_limit, .. }) |
|
Self::Eip4844(TxEip4844 { gas_limit, .. }) |
|
||||||
@ -494,7 +515,10 @@ impl Transaction {
|
|||||||
match self {
|
match self {
|
||||||
Self::Legacy(legacy_tx) => {
|
Self::Legacy(legacy_tx) => {
|
||||||
// do nothing w/ with_header
|
// do nothing w/ with_header
|
||||||
legacy_tx.encode_with_signature(signature, out)
|
legacy_tx.encode_with_signature_fields(
|
||||||
|
&signature.as_signature_with_eip155_parity(legacy_tx.chain_id),
|
||||||
|
out,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Self::Eip2930(access_list_tx) => {
|
Self::Eip2930(access_list_tx) => {
|
||||||
access_list_tx.encode_with_signature(signature, out, with_header)
|
access_list_tx.encode_with_signature(signature, out, with_header)
|
||||||
@ -514,7 +538,7 @@ impl Transaction {
|
|||||||
/// This sets the transaction's gas limit.
|
/// This sets the transaction's gas limit.
|
||||||
pub fn set_gas_limit(&mut self, gas_limit: u64) {
|
pub fn set_gas_limit(&mut self, gas_limit: u64) {
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy(tx) => tx.gas_limit = gas_limit,
|
Self::Legacy(tx) => tx.gas_limit = gas_limit.into(),
|
||||||
Self::Eip2930(tx) => tx.gas_limit = gas_limit,
|
Self::Eip2930(tx) => tx.gas_limit = gas_limit,
|
||||||
Self::Eip1559(tx) => tx.gas_limit = gas_limit,
|
Self::Eip1559(tx) => tx.gas_limit = gas_limit,
|
||||||
Self::Eip4844(tx) => tx.gas_limit = gas_limit,
|
Self::Eip4844(tx) => tx.gas_limit = gas_limit,
|
||||||
@ -1174,7 +1198,9 @@ impl TransactionSigned {
|
|||||||
/// only `true`.
|
/// only `true`.
|
||||||
pub(crate) fn payload_len_inner(&self) -> usize {
|
pub(crate) fn payload_len_inner(&self) -> usize {
|
||||||
match &self.transaction {
|
match &self.transaction {
|
||||||
Transaction::Legacy(legacy_tx) => legacy_tx.payload_len_with_signature(&self.signature),
|
Transaction::Legacy(legacy_tx) => legacy_tx.encoded_len_with_signature(
|
||||||
|
&self.signature.as_signature_with_eip155_parity(legacy_tx.chain_id),
|
||||||
|
),
|
||||||
Transaction::Eip2930(access_list_tx) => {
|
Transaction::Eip2930(access_list_tx) => {
|
||||||
access_list_tx.payload_len_with_signature(&self.signature)
|
access_list_tx.payload_len_with_signature(&self.signature)
|
||||||
}
|
}
|
||||||
@ -1378,7 +1404,9 @@ impl TransactionSigned {
|
|||||||
pub fn length_without_header(&self) -> usize {
|
pub fn length_without_header(&self) -> usize {
|
||||||
// method computes the payload len without a RLP header
|
// method computes the payload len without a RLP header
|
||||||
match &self.transaction {
|
match &self.transaction {
|
||||||
Transaction::Legacy(legacy_tx) => legacy_tx.payload_len_with_signature(&self.signature),
|
Transaction::Legacy(legacy_tx) => legacy_tx.encoded_len_with_signature(
|
||||||
|
&self.signature.as_signature_with_eip155_parity(legacy_tx.chain_id),
|
||||||
|
),
|
||||||
Transaction::Eip2930(access_list_tx) => {
|
Transaction::Eip2930(access_list_tx) => {
|
||||||
access_list_tx.payload_len_with_signature_without_header(&self.signature)
|
access_list_tx.payload_len_with_signature_without_header(&self.signature)
|
||||||
}
|
}
|
||||||
@ -1770,7 +1798,7 @@ mod tests {
|
|||||||
chain_id: Some(4),
|
chain_id: Some(4),
|
||||||
nonce: 1u64,
|
nonce: 1u64,
|
||||||
gas_price: 1000000000,
|
gas_price: 1000000000,
|
||||||
gas_limit: 100000u64,
|
gas_limit: 100000,
|
||||||
to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
|
to: Address::from_slice(&hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046")[..]).into(),
|
||||||
value: U256::from(693361000000000u64),
|
value: U256::from(693361000000000u64),
|
||||||
input: Default::default(),
|
input: Default::default(),
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use crate::{
|
|||||||
TransactionSigned, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, TxHash,
|
TransactionSigned, TransactionSignedEcRecovered, TxEip1559, TxEip2930, TxEip4844, TxHash,
|
||||||
TxLegacy, B256, EIP4844_TX_TYPE_ID,
|
TxLegacy, B256, EIP4844_TX_TYPE_ID,
|
||||||
};
|
};
|
||||||
|
use alloy_consensus::SignableTransaction;
|
||||||
use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE};
|
use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header, EMPTY_LIST_CODE};
|
||||||
use bytes::Buf;
|
use bytes::Buf;
|
||||||
use derive_more::{AsRef, Deref};
|
use derive_more::{AsRef, Deref};
|
||||||
@ -301,7 +302,9 @@ impl PooledTransactionsElement {
|
|||||||
match self {
|
match self {
|
||||||
Self::Legacy { transaction, signature, .. } => {
|
Self::Legacy { transaction, signature, .. } => {
|
||||||
// method computes the payload len with a RLP header
|
// method computes the payload len with a RLP header
|
||||||
transaction.payload_len_with_signature(signature)
|
transaction.encoded_len_with_signature(
|
||||||
|
&signature.as_signature_with_eip155_parity(transaction.chain_id),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Self::Eip2930 { transaction, signature, .. } => {
|
Self::Eip2930 { transaction, signature, .. } => {
|
||||||
// method computes the payload len without a RLP header
|
// method computes the payload len without a RLP header
|
||||||
@ -347,9 +350,11 @@ impl PooledTransactionsElement {
|
|||||||
// - EIP-4844: BlobTransaction::encode_with_type_inner
|
// - EIP-4844: BlobTransaction::encode_with_type_inner
|
||||||
// - EIP-7702: TxEip7702::encode_with_signature
|
// - EIP-7702: TxEip7702::encode_with_signature
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy { transaction, signature, .. } => {
|
Self::Legacy { transaction, signature, .. } => transaction
|
||||||
transaction.encode_with_signature(signature, out)
|
.encode_with_signature_fields(
|
||||||
}
|
&signature.as_signature_with_eip155_parity(transaction.chain_id),
|
||||||
|
out,
|
||||||
|
),
|
||||||
Self::Eip2930 { transaction, signature, .. } => {
|
Self::Eip2930 { transaction, signature, .. } => {
|
||||||
transaction.encode_with_signature(signature, out, false)
|
transaction.encode_with_signature(signature, out, false)
|
||||||
}
|
}
|
||||||
@ -478,9 +483,11 @@ impl Encodable for PooledTransactionsElement {
|
|||||||
// - EIP-4844: BlobTransaction::encode_with_type_inner
|
// - EIP-4844: BlobTransaction::encode_with_type_inner
|
||||||
// - EIP-7702: TxEip7702::encode_with_signature
|
// - EIP-7702: TxEip7702::encode_with_signature
|
||||||
match self {
|
match self {
|
||||||
Self::Legacy { transaction, signature, .. } => {
|
Self::Legacy { transaction, signature, .. } => transaction
|
||||||
transaction.encode_with_signature(signature, out)
|
.encode_with_signature_fields(
|
||||||
}
|
&signature.as_signature_with_eip155_parity(transaction.chain_id),
|
||||||
|
out,
|
||||||
|
),
|
||||||
Self::Eip2930 { transaction, signature, .. } => {
|
Self::Eip2930 { transaction, signature, .. } => {
|
||||||
// encodes with string header
|
// encodes with string header
|
||||||
transaction.encode_with_signature(signature, out, true)
|
transaction.encode_with_signature(signature, out, true)
|
||||||
@ -506,7 +513,9 @@ impl Encodable for PooledTransactionsElement {
|
|||||||
match self {
|
match self {
|
||||||
Self::Legacy { transaction, signature, .. } => {
|
Self::Legacy { transaction, signature, .. } => {
|
||||||
// method computes the payload len with a RLP header
|
// method computes the payload len with a RLP header
|
||||||
transaction.payload_len_with_signature(signature)
|
transaction.encoded_len_with_signature(
|
||||||
|
&signature.as_signature_with_eip155_parity(transaction.chain_id),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Self::Eip2930 { transaction, signature, .. } => {
|
Self::Eip2930 { transaction, signature, .. } => {
|
||||||
// method computes the payload len with a RLP header
|
// method computes the payload len with a RLP header
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use crate::{transaction::util::secp256k1, Address, B256, U256};
|
use crate::{transaction::util::secp256k1, Address, B256, U256};
|
||||||
use alloy_primitives::Bytes;
|
use alloy_consensus::EncodableSignature;
|
||||||
|
use alloy_primitives::{Bytes, Parity};
|
||||||
use alloy_rlp::{Decodable, Encodable, Error as RlpError};
|
use alloy_rlp::{Decodable, Encodable, Error as RlpError};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -18,6 +21,9 @@ const SECP256K1N_HALF: U256 = U256::from_be_bytes([
|
|||||||
/// r, s: Values corresponding to the signature of the
|
/// r, s: Values corresponding to the signature of the
|
||||||
/// transaction and used to determine the sender of
|
/// transaction and used to determine the sender of
|
||||||
/// the transaction; formally Tr and Ts. This is expanded in Appendix F of yellow paper.
|
/// the transaction; formally Tr and Ts. This is expanded in Appendix F of yellow paper.
|
||||||
|
///
|
||||||
|
/// This type is unaware of the chain id, and thus shouldn't be used when encoding or decoding
|
||||||
|
/// legacy transactions. Use `SignatureWithParity` instead.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||||
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
|
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
|
||||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
|
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))]
|
||||||
@ -33,15 +39,6 @@ pub struct Signature {
|
|||||||
pub odd_y_parity: bool,
|
pub odd_y_parity: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signature {
|
|
||||||
/// Returns the signature for the optimism deposit transactions, which don't include a
|
|
||||||
/// signature.
|
|
||||||
#[cfg(feature = "optimism")]
|
|
||||||
pub const fn optimism_deposit_tx_signature() -> Self {
|
|
||||||
Self { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "reth-codec"))]
|
#[cfg(any(test, feature = "reth-codec"))]
|
||||||
impl reth_codecs::Compact for Signature {
|
impl reth_codecs::Compact for Signature {
|
||||||
fn to_compact<B>(&self, buf: &mut B) -> usize
|
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||||
@ -64,44 +61,6 @@ impl reth_codecs::Compact for Signature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Signature {
|
impl Signature {
|
||||||
/// Output the length of the signature without the length of the RLP header, using the legacy
|
|
||||||
/// scheme with EIP-155 support depends on `chain_id`.
|
|
||||||
pub(crate) fn payload_len_with_eip155_chain_id(&self, chain_id: Option<u64>) -> usize {
|
|
||||||
self.v(chain_id).length() + self.r.length() + self.s.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Encode the `v`, `r`, `s` values without a RLP header.
|
|
||||||
/// Encodes the `v` value using the legacy scheme with EIP-155 support depends on `chain_id`.
|
|
||||||
pub(crate) fn encode_with_eip155_chain_id(
|
|
||||||
&self,
|
|
||||||
out: &mut dyn alloy_rlp::BufMut,
|
|
||||||
chain_id: Option<u64>,
|
|
||||||
) {
|
|
||||||
self.v(chain_id).encode(out);
|
|
||||||
self.r.encode(out);
|
|
||||||
self.s.encode(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Output the `v` of the signature depends on `chain_id`
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::missing_const_for_fn)]
|
|
||||||
pub fn v(&self, chain_id: Option<u64>) -> u64 {
|
|
||||||
if let Some(chain_id) = chain_id {
|
|
||||||
// EIP-155: v = {0, 1} + CHAIN_ID * 2 + 35
|
|
||||||
self.odd_y_parity as u64 + chain_id * 2 + 35
|
|
||||||
} else {
|
|
||||||
#[cfg(feature = "optimism")]
|
|
||||||
// pre bedrock system transactions were sent from the zero address as legacy
|
|
||||||
// transactions with an empty signature
|
|
||||||
//
|
|
||||||
// NOTE: this is very hacky and only relevant for op-mainnet pre bedrock
|
|
||||||
if *self == Self::optimism_deposit_tx_signature() {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
self.odd_y_parity as u64 + 27
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decodes the `v`, `r`, `s` values without a RLP header.
|
/// Decodes the `v`, `r`, `s` values without a RLP header.
|
||||||
/// This will return a chain ID if the `v` value is [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) compatible.
|
/// This will return a chain ID if the `v` value is [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) compatible.
|
||||||
pub(crate) fn decode_with_eip155_chain_id(
|
pub(crate) fn decode_with_eip155_chain_id(
|
||||||
@ -202,6 +161,39 @@ impl Signature {
|
|||||||
pub const fn size(&self) -> usize {
|
pub const fn size(&self) -> usize {
|
||||||
core::mem::size_of::<Self>()
|
core::mem::size_of::<Self>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns [Parity] value based on `chain_id` for legacy transaction signature.
|
||||||
|
#[allow(clippy::missing_const_for_fn)]
|
||||||
|
pub fn legacy_parity(&self, chain_id: Option<u64>) -> Parity {
|
||||||
|
if let Some(chain_id) = chain_id {
|
||||||
|
Parity::Parity(self.odd_y_parity).with_chain_id(chain_id)
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
// pre bedrock system transactions were sent from the zero address as legacy
|
||||||
|
// transactions with an empty signature
|
||||||
|
//
|
||||||
|
// NOTE: this is very hacky and only relevant for op-mainnet pre bedrock
|
||||||
|
if *self == Self::optimism_deposit_tx_signature() {
|
||||||
|
return Parity::Parity(false)
|
||||||
|
}
|
||||||
|
Parity::NonEip155(self.odd_y_parity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a signature with the given chain ID applied to the `v` value.
|
||||||
|
pub(crate) fn as_signature_with_eip155_parity(
|
||||||
|
&self,
|
||||||
|
chain_id: Option<u64>,
|
||||||
|
) -> SignatureWithParity {
|
||||||
|
SignatureWithParity::new(self.r, self.s, self.legacy_parity(chain_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the signature for the optimism deposit transactions, which don't include a
|
||||||
|
/// signature.
|
||||||
|
#[cfg(feature = "optimism")]
|
||||||
|
pub const fn optimism_deposit_tx_signature() -> Self {
|
||||||
|
Self { r: U256::ZERO, s: U256::ZERO, odd_y_parity: false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<alloy_primitives::Signature> for Signature {
|
impl From<alloy_primitives::Signature> for Signature {
|
||||||
@ -228,52 +220,69 @@ pub const fn extract_chain_id(v: u64) -> alloy_rlp::Result<(bool, Option<u64>)>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A signature with full parity included.
|
||||||
|
// TODO: replace by alloy Signature when there will be an easy way to instantiate them.
|
||||||
|
pub(crate) struct SignatureWithParity {
|
||||||
|
/// The R field of the signature; the point on the curve.
|
||||||
|
r: U256,
|
||||||
|
/// The S field of the signature; the point on the curve.
|
||||||
|
s: U256,
|
||||||
|
/// Signature parity
|
||||||
|
parity: Parity,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignatureWithParity {
|
||||||
|
/// Creates a new [`SignatureWithParity`].
|
||||||
|
pub(crate) const fn new(r: U256, s: U256, parity: Parity) -> Self {
|
||||||
|
Self { r, s, parity }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncodableSignature for SignatureWithParity {
|
||||||
|
fn from_rs_and_parity<
|
||||||
|
P: TryInto<Parity, Error = E>,
|
||||||
|
E: Into<alloy_primitives::SignatureError>,
|
||||||
|
>(
|
||||||
|
r: U256,
|
||||||
|
s: U256,
|
||||||
|
parity: P,
|
||||||
|
) -> Result<Self, alloy_primitives::SignatureError> {
|
||||||
|
Ok(Self { r, s, parity: parity.try_into().map_err(Into::into)? })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn r(&self) -> U256 {
|
||||||
|
self.r
|
||||||
|
}
|
||||||
|
|
||||||
|
fn s(&self) -> U256 {
|
||||||
|
self.s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn v(&self) -> Parity {
|
||||||
|
self.parity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_parity<T: Into<Parity>>(self, parity: T) -> Self {
|
||||||
|
Self { r: self.r, s: self.s, parity: parity.into() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{transaction::signature::SECP256K1N_HALF, Address, Signature, B256, U256};
|
use crate::{hex, transaction::signature::SECP256K1N_HALF, Address, Signature, B256, U256};
|
||||||
use alloy_primitives::{hex, hex::FromHex, Bytes};
|
use alloy_primitives::{hex::FromHex, Bytes, Parity};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_payload_len_with_eip155_chain_id() {
|
fn test_legacy_parity() {
|
||||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
||||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
||||||
|
assert_eq!(Parity::NonEip155(false), signature.legacy_parity(None));
|
||||||
assert_eq!(3, signature.payload_len_with_eip155_chain_id(None));
|
assert_eq!(Parity::Eip155(37), signature.legacy_parity(Some(1)));
|
||||||
assert_eq!(3, signature.payload_len_with_eip155_chain_id(Some(1)));
|
|
||||||
assert_eq!(4, signature.payload_len_with_eip155_chain_id(Some(47)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_v() {
|
|
||||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
|
||||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
|
||||||
assert_eq!(27, signature.v(None));
|
|
||||||
assert_eq!(37, signature.v(Some(1)));
|
|
||||||
|
|
||||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: true };
|
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: true };
|
||||||
assert_eq!(28, signature.v(None));
|
assert_eq!(Parity::NonEip155(true), signature.legacy_parity(None));
|
||||||
assert_eq!(38, signature.v(Some(1)));
|
assert_eq!(Parity::Eip155(38), signature.legacy_parity(Some(1)));
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_encode_and_decode_with_eip155_chain_id() {
|
|
||||||
// Select 1 as an arbitrary nonzero value for R and S, as v() always returns 0 for (0, 0).
|
|
||||||
let signature = Signature { r: U256::from(1), s: U256::from(1), odd_y_parity: false };
|
|
||||||
|
|
||||||
let mut encoded = Vec::new();
|
|
||||||
signature.encode_with_eip155_chain_id(&mut encoded, None);
|
|
||||||
assert_eq!(encoded.len(), signature.payload_len_with_eip155_chain_id(None));
|
|
||||||
let (decoded, chain_id) = Signature::decode_with_eip155_chain_id(&mut &*encoded).unwrap();
|
|
||||||
assert_eq!(signature, decoded);
|
|
||||||
assert_eq!(None, chain_id);
|
|
||||||
|
|
||||||
let mut encoded = Vec::new();
|
|
||||||
signature.encode_with_eip155_chain_id(&mut encoded, Some(1));
|
|
||||||
assert_eq!(encoded.len(), signature.payload_len_with_eip155_chain_id(Some(1)));
|
|
||||||
let (decoded, chain_id) = Signature::decode_with_eip155_chain_id(&mut &*encoded).unwrap();
|
|
||||||
assert_eq!(signature, decoded);
|
|
||||||
assert_eq!(Some(1), chain_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -13,7 +13,7 @@ pub(crate) fn from_legacy_primitive_signature(
|
|||||||
Signature {
|
Signature {
|
||||||
r: signature.r,
|
r: signature.r,
|
||||||
s: signature.s,
|
s: signature.s,
|
||||||
v: U256::from(signature.v(chain_id)),
|
v: U256::from(signature.legacy_parity(chain_id).to_u64()),
|
||||||
y_parity: None,
|
y_parity: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,7 +31,11 @@ alloy-eips = { workspace = true, default-features = false, features = [
|
|||||||
"arbitrary",
|
"arbitrary",
|
||||||
"serde",
|
"serde",
|
||||||
] }
|
] }
|
||||||
alloy-primitives = { workspace = true, features = ["arbitrary", "serde", "rand"] }
|
alloy-primitives = { workspace = true, features = [
|
||||||
|
"arbitrary",
|
||||||
|
"serde",
|
||||||
|
"rand",
|
||||||
|
] }
|
||||||
alloy-consensus = { workspace = true, features = ["arbitrary"] }
|
alloy-consensus = { workspace = true, features = ["arbitrary"] }
|
||||||
test-fuzz.workspace = true
|
test-fuzz.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@ -27,7 +27,7 @@ pub fn maybe_generate_tests(
|
|||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
let len = field.clone().to_compact(&mut buf);
|
let len = field.clone().to_compact(&mut buf);
|
||||||
let (decoded, _): (super::#type_ident, _) = Compact::from_compact(&buf, len);
|
let (decoded, _): (super::#type_ident, _) = Compact::from_compact(&buf, len);
|
||||||
assert!(field == decoded, "maybe_generate_tests::compact");
|
assert_eq!(field, decoded, "maybe_generate_tests::compact");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if arg.to_string() == "rlp" {
|
} else if arg.to_string() == "rlp" {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ mod authorization_list;
|
|||||||
mod genesis_account;
|
mod genesis_account;
|
||||||
mod log;
|
mod log;
|
||||||
mod request;
|
mod request;
|
||||||
|
mod transaction;
|
||||||
mod trie;
|
mod trie;
|
||||||
mod txkind;
|
mod txkind;
|
||||||
mod withdrawal;
|
mod withdrawal;
|
||||||
|
|||||||
94
crates/storage/codecs/src/alloy/transaction.rs
Normal file
94
crates/storage/codecs/src/alloy/transaction.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use crate::Compact;
|
||||||
|
use alloy_consensus::TxLegacy as AlloyTxLegacy;
|
||||||
|
use alloy_primitives::{Bytes, ChainId, TxKind, U256};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Legacy transaction.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Default, Compact, Serialize, Deserialize)]
|
||||||
|
#[cfg_attr(test, derive(arbitrary::Arbitrary))]
|
||||||
|
#[cfg_attr(test, crate::add_arbitrary_tests(compact))]
|
||||||
|
struct TxLegacy {
|
||||||
|
/// Added as EIP-155: Simple replay attack protection
|
||||||
|
chain_id: Option<ChainId>,
|
||||||
|
/// A scalar value equal to the number of transactions sent by the sender; formally Tn.
|
||||||
|
nonce: u64,
|
||||||
|
/// A scalar value equal to the number of
|
||||||
|
/// Wei to be paid per unit of gas for all computation
|
||||||
|
/// costs incurred as a result of the execution of this transaction; formally Tp.
|
||||||
|
///
|
||||||
|
/// As ethereum circulation is around 120mil eth as of 2022 that is around
|
||||||
|
/// 120000000000000000000000000 wei we are safe to use u128 as its max number is:
|
||||||
|
/// 340282366920938463463374607431768211455
|
||||||
|
gas_price: u128,
|
||||||
|
/// A scalar value equal to the maximum
|
||||||
|
/// amount of gas that should be used in executing
|
||||||
|
/// this transaction. This is paid up-front, before any
|
||||||
|
/// computation is done and may not be increased
|
||||||
|
/// later; formally Tg.
|
||||||
|
gas_limit: u64,
|
||||||
|
/// The 160-bit address of the message call’s recipient or, for a contract creation
|
||||||
|
/// transaction, ∅, used here to denote the only member of B0 ; formally Tt.
|
||||||
|
to: TxKind,
|
||||||
|
/// A scalar value equal to the number of Wei to
|
||||||
|
/// be transferred to the message call’s recipient or,
|
||||||
|
/// in the case of contract creation, as an endowment
|
||||||
|
/// to the newly created account; formally Tv.
|
||||||
|
value: U256,
|
||||||
|
/// Input has two uses depending if transaction is Create or Call (if `to` field is None or
|
||||||
|
/// Some). pub init: An unlimited size byte array specifying the
|
||||||
|
/// EVM-code for the account initialisation procedure CREATE,
|
||||||
|
/// data: An unlimited size byte array specifying the
|
||||||
|
/// input data of the message call, formally Td.
|
||||||
|
input: Bytes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compact for AlloyTxLegacy {
|
||||||
|
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||||
|
where
|
||||||
|
B: bytes::BufMut + AsMut<[u8]>,
|
||||||
|
{
|
||||||
|
let tx = TxLegacy {
|
||||||
|
chain_id: self.chain_id,
|
||||||
|
nonce: self.nonce,
|
||||||
|
gas_price: self.gas_price,
|
||||||
|
gas_limit: self.gas_limit as u64,
|
||||||
|
to: self.to,
|
||||||
|
value: self.value,
|
||||||
|
input: self.input.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
tx.to_compact(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
|
||||||
|
let (tx, _) = TxLegacy::from_compact(buf, len);
|
||||||
|
|
||||||
|
let alloy_tx = Self {
|
||||||
|
chain_id: tx.chain_id,
|
||||||
|
nonce: tx.nonce,
|
||||||
|
gas_price: tx.gas_price,
|
||||||
|
gas_limit: tx.gas_limit as u128,
|
||||||
|
to: tx.to,
|
||||||
|
value: tx.value,
|
||||||
|
input: tx.input,
|
||||||
|
};
|
||||||
|
|
||||||
|
(alloy_tx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::alloy::transaction::TxLegacy;
|
||||||
|
|
||||||
|
// each value in the database has an extra field named flags that encodes metadata about other
|
||||||
|
// fields in the value, e.g. offset and length.
|
||||||
|
//
|
||||||
|
// this check is to ensure we do not inadvertently add too many fields to a struct which would
|
||||||
|
// expand the flags field and break backwards compatibility
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ensure_backwards_compatibility() {
|
||||||
|
assert_eq!(TxLegacy::bitflag_encoded_bytes(), 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -303,7 +303,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
Account, Header, Receipt, ReceiptWithBloom, SealedHeader, TxEip1559, TxEip2930, TxEip4844,
|
Account, Header, Receipt, ReceiptWithBloom, SealedHeader, TxEip1559, TxEip2930, TxEip4844,
|
||||||
TxLegacy, Withdrawals,
|
Withdrawals,
|
||||||
};
|
};
|
||||||
use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment};
|
use reth_prune_types::{PruneCheckpoint, PruneMode, PruneSegment};
|
||||||
use reth_stages_types::{
|
use reth_stages_types::{
|
||||||
@ -346,7 +346,6 @@ mod tests {
|
|||||||
assert_eq!(TxEip1559::bitflag_encoded_bytes(), 4);
|
assert_eq!(TxEip1559::bitflag_encoded_bytes(), 4);
|
||||||
assert_eq!(TxEip2930::bitflag_encoded_bytes(), 3);
|
assert_eq!(TxEip2930::bitflag_encoded_bytes(), 3);
|
||||||
assert_eq!(TxEip4844::bitflag_encoded_bytes(), 5);
|
assert_eq!(TxEip4844::bitflag_encoded_bytes(), 5);
|
||||||
assert_eq!(TxLegacy::bitflag_encoded_bytes(), 3);
|
|
||||||
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +378,6 @@ mod tests {
|
|||||||
assert_eq!(TxEip1559::bitflag_encoded_bytes(), 4);
|
assert_eq!(TxEip1559::bitflag_encoded_bytes(), 4);
|
||||||
assert_eq!(TxEip2930::bitflag_encoded_bytes(), 3);
|
assert_eq!(TxEip2930::bitflag_encoded_bytes(), 3);
|
||||||
assert_eq!(TxEip4844::bitflag_encoded_bytes(), 5);
|
assert_eq!(TxEip4844::bitflag_encoded_bytes(), 5);
|
||||||
assert_eq!(TxLegacy::bitflag_encoded_bytes(), 3);
|
|
||||||
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
assert_eq!(Withdrawals::bitflag_encoded_bytes(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -143,7 +143,7 @@ impl TransactionBuilder {
|
|||||||
TxLegacy {
|
TxLegacy {
|
||||||
chain_id: Some(self.chain_id),
|
chain_id: Some(self.chain_id),
|
||||||
nonce: self.nonce,
|
nonce: self.nonce,
|
||||||
gas_limit: self.gas_limit,
|
gas_limit: self.gas_limit.into(),
|
||||||
gas_price: self.max_fee_per_gas,
|
gas_price: self.max_fee_per_gas,
|
||||||
to: self.to,
|
to: self.to,
|
||||||
value: self.value,
|
value: self.value,
|
||||||
|
|||||||
@ -798,7 +798,7 @@ impl TryFrom<TransactionSignedEcRecovered> for MockTransaction {
|
|||||||
sender,
|
sender,
|
||||||
nonce,
|
nonce,
|
||||||
gas_price,
|
gas_price,
|
||||||
gas_limit,
|
gas_limit: gas_limit as u64,
|
||||||
to,
|
to,
|
||||||
value,
|
value,
|
||||||
input,
|
input,
|
||||||
@ -919,7 +919,15 @@ impl From<MockTransaction> for Transaction {
|
|||||||
value,
|
value,
|
||||||
input,
|
input,
|
||||||
size: _,
|
size: _,
|
||||||
} => Self::Legacy(TxLegacy { chain_id, nonce, gas_price, gas_limit, to, value, input }),
|
} => Self::Legacy(TxLegacy {
|
||||||
|
chain_id,
|
||||||
|
nonce,
|
||||||
|
gas_price,
|
||||||
|
gas_limit: gas_limit.into(),
|
||||||
|
to,
|
||||||
|
value,
|
||||||
|
input,
|
||||||
|
}),
|
||||||
MockTransaction::Eip2930 {
|
MockTransaction::Eip2930 {
|
||||||
chain_id,
|
chain_id,
|
||||||
hash: _,
|
hash: _,
|
||||||
@ -1023,7 +1031,7 @@ impl proptest::arbitrary::Arbitrary for MockTransaction {
|
|||||||
hash: tx_hash,
|
hash: tx_hash,
|
||||||
nonce: *nonce,
|
nonce: *nonce,
|
||||||
gas_price: *gas_price,
|
gas_price: *gas_price,
|
||||||
gas_limit: *gas_limit,
|
gas_limit: *gas_limit as u64,
|
||||||
to: *to,
|
to: *to,
|
||||||
value: *value,
|
value: *value,
|
||||||
input: input.clone(),
|
input: input.clone(),
|
||||||
|
|||||||
Reference in New Issue
Block a user