mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: ethereum-specific receipt (#13295)
This commit is contained in:
@ -12,7 +12,44 @@ description = "Ethereum primitive types"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
# reth
|
||||
reth-codecs = { workspace = true, optional = true }
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-zstd-compressors = { workspace = true, optional = true }
|
||||
|
||||
# ethereum
|
||||
alloy-primitives.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
alloy-rlp.workspace = true
|
||||
|
||||
# misc
|
||||
arbitrary = { workspace = true, optional = true, features = ["derive"] }
|
||||
modular-bitfield = { workspace = true, optional = true }
|
||||
serde.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
test-fuzz.workspace = true
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = []
|
||||
std = [
|
||||
"alloy-consensus/std",
|
||||
"alloy-primitives/std",
|
||||
"alloy-rlp/std",
|
||||
"reth-primitives-traits/std",
|
||||
"reth-zstd-compressors?/std",
|
||||
"serde/std"
|
||||
]
|
||||
reth-codec = [
|
||||
"std",
|
||||
"dep:reth-codecs",
|
||||
"dep:modular-bitfield",
|
||||
"dep:reth-zstd-compressors",
|
||||
]
|
||||
arbitrary = [
|
||||
"dep:arbitrary",
|
||||
"alloy-consensus/arbitrary",
|
||||
"alloy-primitives/arbitrary",
|
||||
"reth-codecs?/arbitrary",
|
||||
"reth-primitives-traits/arbitrary"
|
||||
]
|
||||
|
||||
@ -8,3 +8,8 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
|
||||
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod receipt;
|
||||
pub use receipt::*;
|
||||
|
||||
209
crates/ethereum/primitives/src/receipt.rs
Normal file
209
crates/ethereum/primitives/src/receipt.rs
Normal file
@ -0,0 +1,209 @@
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{
|
||||
Eip2718EncodableReceipt, Eip658Value, ReceiptWithBloom, RlpDecodableReceipt,
|
||||
RlpEncodableReceipt, TxReceipt, TxType, Typed2718,
|
||||
};
|
||||
use alloy_primitives::{Bloom, Log};
|
||||
use alloy_rlp::{BufMut, Decodable, Encodable, Header};
|
||||
use reth_primitives_traits::InMemorySize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Typed ethereum transaction receipt.
|
||||
/// Receipt containing result of transaction execution.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[cfg_attr(feature = "reth-codec", derive(reth_codecs::CompactZstd))]
|
||||
#[cfg_attr(feature = "reth-codec", reth_codecs::add_arbitrary_tests)]
|
||||
#[cfg_attr(feature = "reth-codec", reth_zstd(
|
||||
compressor = reth_zstd_compressors::RECEIPT_COMPRESSOR,
|
||||
decompressor = reth_zstd_compressors::RECEIPT_DECOMPRESSOR
|
||||
))]
|
||||
pub struct Receipt {
|
||||
/// Receipt type.
|
||||
#[serde(with = "tx_type_serde")]
|
||||
pub tx_type: TxType,
|
||||
/// If transaction is executed successfully.
|
||||
///
|
||||
/// This is the `statusCode`
|
||||
pub success: bool,
|
||||
/// Gas used
|
||||
pub cumulative_gas_used: u64,
|
||||
/// Log send from contracts.
|
||||
pub logs: Vec<Log>,
|
||||
}
|
||||
|
||||
impl Receipt {
|
||||
/// 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 {
|
||||
self.success.length() +
|
||||
self.cumulative_gas_used.length() +
|
||||
bloom.length() +
|
||||
self.logs.length()
|
||||
}
|
||||
|
||||
/// RLP-encodes receipt fields with the given [`Bloom`] without an RLP header.
|
||||
pub fn rlp_encode_fields(&self, bloom: &Bloom, out: &mut dyn BufMut) {
|
||||
self.success.encode(out);
|
||||
self.cumulative_gas_used.encode(out);
|
||||
bloom.encode(out);
|
||||
self.logs.encode(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: TxType,
|
||||
) -> alloy_rlp::Result<ReceiptWithBloom<Self>> {
|
||||
let header = Header::decode(buf)?;
|
||||
if !header.list {
|
||||
return Err(alloy_rlp::Error::UnexpectedString);
|
||||
}
|
||||
|
||||
let remaining = buf.len();
|
||||
|
||||
let success = Decodable::decode(buf)?;
|
||||
let cumulative_gas_used = Decodable::decode(buf)?;
|
||||
let logs_bloom = Decodable::decode(buf)?;
|
||||
let logs = Decodable::decode(buf)?;
|
||||
|
||||
if buf.len() + header.payload_length != remaining {
|
||||
return Err(alloy_rlp::Error::UnexpectedLength);
|
||||
}
|
||||
|
||||
Ok(ReceiptWithBloom {
|
||||
receipt: Self { cumulative_gas_used, tx_type, success, logs },
|
||||
logs_bloom,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Eip2718EncodableReceipt for Receipt {
|
||||
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 Receipt {
|
||||
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 Receipt {
|
||||
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, TxType::Legacy)
|
||||
}
|
||||
|
||||
// Otherwise, advance the buffer and try decoding type flag followed by receipt
|
||||
*buf = *header_buf;
|
||||
|
||||
let remaining = buf.len();
|
||||
let tx_type = TxType::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 Receipt {
|
||||
type Log = Log;
|
||||
|
||||
fn status_or_post_state(&self) -> Eip658Value {
|
||||
self.success.into()
|
||||
}
|
||||
|
||||
fn status(&self) -> bool {
|
||||
self.success
|
||||
}
|
||||
|
||||
fn bloom(&self) -> Bloom {
|
||||
alloy_primitives::logs_bloom(self.logs())
|
||||
}
|
||||
|
||||
fn cumulative_gas_used(&self) -> u128 {
|
||||
self.cumulative_gas_used as u128
|
||||
}
|
||||
|
||||
fn logs(&self) -> &[Log] {
|
||||
&self.logs
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed2718 for Receipt {
|
||||
fn ty(&self) -> u8 {
|
||||
self.tx_type as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for Receipt {
|
||||
fn size(&self) -> usize {
|
||||
self.tx_type.size() +
|
||||
core::mem::size_of::<bool>() +
|
||||
core::mem::size_of::<u64>() +
|
||||
self.logs.capacity() * core::mem::size_of::<Log>()
|
||||
}
|
||||
}
|
||||
|
||||
impl reth_primitives_traits::Receipt for Receipt {}
|
||||
|
||||
/// TODO: Remove once <https://github.com/alloy-rs/alloy/pull/1780> is released.
|
||||
mod tx_type_serde {
|
||||
use alloy_primitives::{U64, U8};
|
||||
|
||||
use super::*;
|
||||
|
||||
pub(crate) fn serialize<S>(tx_type: &TxType, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
let value: U8 = (*tx_type).into();
|
||||
value.serialize(serializer)
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<TxType, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
U64::deserialize(deserializer)?.try_into().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user