mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: optimism-specific receipt (#13317)
Co-authored-by: Emilia Hane <elsaemiliaevahane@gmail.com>
This commit is contained in:
@ -16,6 +16,7 @@ workspace = true
|
||||
reth-primitives.workspace = true
|
||||
reth-primitives-traits = { workspace = true, features = ["op"] }
|
||||
reth-codecs = { workspace = true, optional = true, features = ["op"] }
|
||||
reth-zstd-compressors = { workspace = true, optional = true }
|
||||
|
||||
# ethereum
|
||||
alloy-primitives.workspace = true
|
||||
@ -30,6 +31,7 @@ op-alloy-consensus.workspace = true
|
||||
|
||||
# codec
|
||||
bytes = { workspace = true, optional = true }
|
||||
modular-bitfield = { workspace = true, optional = true }
|
||||
serde = { workspace = true, optional = true }
|
||||
|
||||
# misc
|
||||
@ -59,9 +61,10 @@ std = [
|
||||
"serde?/std",
|
||||
"bytes?/std",
|
||||
"derive_more/std",
|
||||
"revm-primitives/std",
|
||||
"secp256k1?/std",
|
||||
"alloy-rlp/std",
|
||||
"revm-primitives/std",
|
||||
"secp256k1?/std",
|
||||
"alloy-rlp/std",
|
||||
"reth-zstd-compressors?/std"
|
||||
]
|
||||
reth-codec = [
|
||||
"dep:reth-codecs",
|
||||
@ -74,6 +77,8 @@ reth-codec = [
|
||||
"reth-codecs?/op",
|
||||
"reth-primitives/reth-codec",
|
||||
"dep:bytes",
|
||||
"dep:modular-bitfield",
|
||||
"dep:reth-zstd-compressors"
|
||||
]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
|
||||
@ -18,6 +18,9 @@ pub mod transaction;
|
||||
|
||||
pub use transaction::{signed::OpTransactionSigned, tx_type::OpTxType, OpTransaction};
|
||||
|
||||
mod receipt;
|
||||
pub use receipt::OpReceipt;
|
||||
|
||||
/// Optimism primitive types.
|
||||
pub type OpPrimitives = reth_primitives::EthPrimitives;
|
||||
|
||||
|
||||
299
crates/optimism/primitives/src/receipt.rs
Normal file
299
crates/optimism/primitives/src/receipt.rs
Normal file
@ -0,0 +1,299 @@
|
||||
use alloy_consensus::{
|
||||
Eip2718EncodableReceipt, Eip658Value, Receipt, ReceiptWithBloom, RlpDecodableReceipt,
|
||||
RlpEncodableReceipt, TxReceipt, Typed2718,
|
||||
};
|
||||
use alloy_primitives::{Bloom, Log};
|
||||
use alloy_rlp::{BufMut, Decodable, Header};
|
||||
use op_alloy_consensus::{OpDepositReceipt, OpTxType};
|
||||
use reth_primitives_traits::InMemorySize;
|
||||
|
||||
/// Typed ethereum transaction receipt.
|
||||
/// Receipt containing result of transaction execution.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
pub enum OpReceipt {
|
||||
/// Legacy receipt
|
||||
Legacy(Receipt),
|
||||
/// EIP-2930 receipt
|
||||
Eip2930(Receipt),
|
||||
/// EIP-1559 receipt
|
||||
Eip1559(Receipt),
|
||||
/// EIP-7702 receipt
|
||||
Eip7702(Receipt),
|
||||
/// Deposit receipt
|
||||
Deposit(OpDepositReceipt),
|
||||
}
|
||||
|
||||
impl OpReceipt {
|
||||
/// Returns [`OpTxType`] of the receipt.
|
||||
pub const fn tx_type(&self) -> OpTxType {
|
||||
match self {
|
||||
Self::Legacy(_) => OpTxType::Legacy,
|
||||
Self::Eip2930(_) => OpTxType::Eip2930,
|
||||
Self::Eip1559(_) => OpTxType::Eip1559,
|
||||
Self::Eip7702(_) => OpTxType::Eip7702,
|
||||
Self::Deposit(_) => OpTxType::Deposit,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns inner [`Receipt`],
|
||||
pub const fn as_receipt(&self) -> &Receipt {
|
||||
match self {
|
||||
Self::Legacy(receipt) |
|
||||
Self::Eip2930(receipt) |
|
||||
Self::Eip1559(receipt) |
|
||||
Self::Eip7702(receipt) => receipt,
|
||||
Self::Deposit(receipt) => &receipt.inner,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns length of RLP-encoded receipt fields with the given [`Bloom`] without an RLP header.
|
||||
pub fn rlp_encoded_fields_length(&self, bloom: &Bloom) -> usize {
|
||||
match self {
|
||||
Self::Legacy(receipt) |
|
||||
Self::Eip2930(receipt) |
|
||||
Self::Eip1559(receipt) |
|
||||
Self::Eip7702(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom),
|
||||
Self::Deposit(receipt) => receipt.rlp_encoded_fields_length_with_bloom(bloom),
|
||||
}
|
||||
}
|
||||
|
||||
/// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header.
|
||||
pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) {
|
||||
match self {
|
||||
Self::Legacy(receipt) |
|
||||
Self::Eip2930(receipt) |
|
||||
Self::Eip1559(receipt) |
|
||||
Self::Eip7702(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out),
|
||||
Self::Deposit(receipt) => receipt.rlp_encode_fields_with_bloom(bloom, out),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns RLP header for inner encoding.
|
||||
pub fn rlp_header_inner(&self, bloom: &Bloom) -> Header {
|
||||
Header { list: true, payload_length: self.rlp_encoded_fields_length(bloom) }
|
||||
}
|
||||
|
||||
/// RLP-decodes the receipt from the provided buffer. This does not expect a type byte or
|
||||
/// network header.
|
||||
pub fn rlp_decode_inner(
|
||||
buf: &mut &[u8],
|
||||
tx_type: OpTxType,
|
||||
) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
|
||||
match tx_type {
|
||||
OpTxType::Legacy => {
|
||||
let ReceiptWithBloom { receipt, logs_bloom } =
|
||||
RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
|
||||
Ok(ReceiptWithBloom { receipt: Self::Legacy(receipt), logs_bloom })
|
||||
}
|
||||
OpTxType::Eip2930 => {
|
||||
let ReceiptWithBloom { receipt, logs_bloom } =
|
||||
RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
|
||||
Ok(ReceiptWithBloom { receipt: Self::Eip2930(receipt), logs_bloom })
|
||||
}
|
||||
OpTxType::Eip1559 => {
|
||||
let ReceiptWithBloom { receipt, logs_bloom } =
|
||||
RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
|
||||
Ok(ReceiptWithBloom { receipt: Self::Eip1559(receipt), logs_bloom })
|
||||
}
|
||||
OpTxType::Eip7702 => {
|
||||
let ReceiptWithBloom { receipt, logs_bloom } =
|
||||
RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
|
||||
Ok(ReceiptWithBloom { receipt: Self::Eip7702(receipt), logs_bloom })
|
||||
}
|
||||
OpTxType::Deposit => {
|
||||
let ReceiptWithBloom { receipt, logs_bloom } =
|
||||
RlpDecodableReceipt::rlp_decode_with_bloom(buf)?;
|
||||
Ok(ReceiptWithBloom { receipt: Self::Deposit(receipt), logs_bloom })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eip2718EncodableReceipt for OpReceipt {
|
||||
fn eip2718_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
|
||||
!self.tx_type().is_legacy() as usize + self.rlp_header_inner(bloom).length_with_payload()
|
||||
}
|
||||
|
||||
fn eip2718_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
|
||||
if !self.tx_type().is_legacy() {
|
||||
out.put_u8(self.tx_type() as u8);
|
||||
}
|
||||
self.rlp_header_inner(bloom).encode(out);
|
||||
self.rlp_encode_fields(bloom, out);
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpEncodableReceipt for OpReceipt {
|
||||
fn rlp_encoded_length_with_bloom(&self, bloom: &Bloom) -> usize {
|
||||
let mut len = self.eip2718_encoded_length_with_bloom(bloom);
|
||||
if !self.tx_type().is_legacy() {
|
||||
len += Header {
|
||||
list: false,
|
||||
payload_length: self.eip2718_encoded_length_with_bloom(bloom),
|
||||
}
|
||||
.length();
|
||||
}
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
fn rlp_encode_with_bloom(&self, bloom: &Bloom, out: &mut dyn BufMut) {
|
||||
if !self.tx_type().is_legacy() {
|
||||
Header { list: false, payload_length: self.eip2718_encoded_length_with_bloom(bloom) }
|
||||
.encode(out);
|
||||
}
|
||||
self.eip2718_encode_with_bloom(bloom, out);
|
||||
}
|
||||
}
|
||||
|
||||
impl RlpDecodableReceipt for OpReceipt {
|
||||
fn rlp_decode_with_bloom(buf: &mut &[u8]) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
|
||||
let header_buf = &mut &**buf;
|
||||
let header = Header::decode(header_buf)?;
|
||||
|
||||
// Legacy receipt, reuse initial buffer without advancing
|
||||
if header.list {
|
||||
return Self::rlp_decode_inner(buf, OpTxType::Legacy)
|
||||
}
|
||||
|
||||
// Otherwise, advance the buffer and try decoding type flag followed by receipt
|
||||
*buf = *header_buf;
|
||||
|
||||
let remaining = buf.len();
|
||||
let tx_type = OpTxType::decode(buf)?;
|
||||
let this = Self::rlp_decode_inner(buf, tx_type)?;
|
||||
|
||||
if buf.len() + header.payload_length != remaining {
|
||||
return Err(alloy_rlp::Error::UnexpectedLength);
|
||||
}
|
||||
|
||||
Ok(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxReceipt for OpReceipt {
|
||||
type Log = Log;
|
||||
|
||||
fn status_or_post_state(&self) -> Eip658Value {
|
||||
self.as_receipt().status_or_post_state()
|
||||
}
|
||||
|
||||
fn status(&self) -> bool {
|
||||
self.as_receipt().status()
|
||||
}
|
||||
|
||||
fn bloom(&self) -> Bloom {
|
||||
self.as_receipt().bloom()
|
||||
}
|
||||
|
||||
fn cumulative_gas_used(&self) -> u128 {
|
||||
self.as_receipt().cumulative_gas_used()
|
||||
}
|
||||
|
||||
fn logs(&self) -> &[Log] {
|
||||
self.as_receipt().logs()
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed2718 for OpReceipt {
|
||||
fn ty(&self) -> u8 {
|
||||
self.tx_type().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for OpReceipt {
|
||||
fn size(&self) -> usize {
|
||||
self.as_receipt().size()
|
||||
}
|
||||
}
|
||||
|
||||
impl reth_primitives_traits::Receipt for OpReceipt {}
|
||||
|
||||
#[cfg(feature = "reth-codec")]
|
||||
mod compact {
|
||||
use super::*;
|
||||
use alloc::borrow::Cow;
|
||||
use reth_codecs::Compact;
|
||||
|
||||
#[derive(reth_codecs::CompactZstd)]
|
||||
#[reth_zstd(
|
||||
compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR,
|
||||
decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR
|
||||
)]
|
||||
struct CompactOpReceipt<'a> {
|
||||
tx_type: OpTxType,
|
||||
success: bool,
|
||||
cumulative_gas_used: u64,
|
||||
logs: Cow<'a, Vec<Log>>,
|
||||
deposit_nonce: Option<u64>,
|
||||
deposit_receipt_version: Option<u64>,
|
||||
}
|
||||
|
||||
impl<'a> From<&'a OpReceipt> for CompactOpReceipt<'a> {
|
||||
fn from(receipt: &'a OpReceipt) -> Self {
|
||||
Self {
|
||||
tx_type: receipt.tx_type(),
|
||||
success: receipt.status(),
|
||||
cumulative_gas_used: receipt.cumulative_gas_used() as u64,
|
||||
logs: Cow::Borrowed(&receipt.as_receipt().logs),
|
||||
deposit_nonce: if let OpReceipt::Deposit(receipt) = receipt {
|
||||
receipt.deposit_nonce
|
||||
} else {
|
||||
None
|
||||
},
|
||||
deposit_receipt_version: if let OpReceipt::Deposit(receipt) = receipt {
|
||||
receipt.deposit_receipt_version
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CompactOpReceipt<'_>> for OpReceipt {
|
||||
fn from(receipt: CompactOpReceipt<'_>) -> Self {
|
||||
let CompactOpReceipt {
|
||||
tx_type,
|
||||
success,
|
||||
cumulative_gas_used,
|
||||
logs,
|
||||
deposit_nonce,
|
||||
deposit_receipt_version,
|
||||
} = receipt;
|
||||
|
||||
let inner = Receipt {
|
||||
status: success.into(),
|
||||
cumulative_gas_used: cumulative_gas_used as u128,
|
||||
logs: logs.into_owned(),
|
||||
};
|
||||
|
||||
match tx_type {
|
||||
OpTxType::Legacy => Self::Legacy(inner),
|
||||
OpTxType::Eip2930 => Self::Eip2930(inner),
|
||||
OpTxType::Eip1559 => Self::Eip1559(inner),
|
||||
OpTxType::Eip7702 => Self::Eip7702(inner),
|
||||
OpTxType::Deposit => Self::Deposit(OpDepositReceipt {
|
||||
inner,
|
||||
deposit_nonce,
|
||||
deposit_receipt_version,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Compact for OpReceipt {
|
||||
fn to_compact<B>(&self, buf: &mut B) -> usize
|
||||
where
|
||||
B: bytes::BufMut + AsMut<[u8]>,
|
||||
{
|
||||
CompactOpReceipt::from(self).to_compact(buf)
|
||||
}
|
||||
|
||||
fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
|
||||
let (receipt, buf) = CompactOpReceipt::from_compact(buf, len);
|
||||
(receipt.into(), buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ use std::sync::OnceLock;
|
||||
#[derive(Debug, Clone, Eq, AsRef, Deref)]
|
||||
pub struct OpTransactionSigned {
|
||||
/// Transaction hash
|
||||
#[serde(skip)]
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub hash: OnceLock<TxHash>,
|
||||
/// The transaction signature values
|
||||
pub signature: Signature,
|
||||
|
||||
Reference in New Issue
Block a user