Skip to content

Commit

Permalink
Fix: Support pre-658 status codes (#848)
Browse files Browse the repository at this point in the history
* feature: Eip658Value

* feat: support pre-658 receipts

* chore: match previous behavior more closely

* fix: incorrect decode impl

* lint: fmt

* fix: root

* test: add one for root/status
  • Loading branch information
prestwich authored Jun 7, 2024
1 parent f6ebef2 commit 506edee
Show file tree
Hide file tree
Showing 7 changed files with 351 additions and 26 deletions.
4 changes: 3 additions & 1 deletion crates/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ mod header;
pub use header::{Header, EMPTY_OMMER_ROOT_HASH, EMPTY_ROOT_HASH};

mod receipt;
pub use receipt::{AnyReceiptEnvelope, Receipt, ReceiptEnvelope, ReceiptWithBloom, TxReceipt};
pub use receipt::{
AnyReceiptEnvelope, Eip658Value, Receipt, ReceiptEnvelope, ReceiptWithBloom, TxReceipt,
};

mod request;
pub use request::Request;
Expand Down
28 changes: 21 additions & 7 deletions crates/consensus/src/receipt/any.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{ReceiptWithBloom, TxReceipt};
use crate::{Eip658Value, ReceiptWithBloom, TxReceipt};
use alloy_eips::eip2718::{Decodable2718, Encodable2718};
use alloy_primitives::{bytes::BufMut, Bloom, Log};
use alloy_rlp::{Decodable, Encodable};
Expand Down Expand Up @@ -47,13 +47,27 @@ impl<T> AnyReceiptEnvelope<T> {
}

/// Return true if the transaction was successful.
///
/// ## Note
///
/// This method may not accurately reflect the status of the transaction
/// for transactions before [EIP-658].
///
/// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658
pub const fn is_success(&self) -> bool {
self.status()
}

/// Returns the success status of the receipt's transaction.
///
/// ## Note
///
/// This method may not accurately reflect the status of the transaction
/// for transactions before [EIP-658].
///
/// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658
pub const fn status(&self) -> bool {
self.inner.receipt.status
matches!(self.inner.receipt.status, Eip658Value::Eip658(true) | Eip658Value::PostState(_))
}

/// Return the receipt's bloom.
Expand All @@ -73,22 +87,22 @@ impl<T> AnyReceiptEnvelope<T> {
}

impl<T> TxReceipt<T> for AnyReceiptEnvelope<T> {
/// Returns the success status of the receipt's transaction.
fn status_or_post_state(&self) -> &Eip658Value {
self.inner.status_or_post_state()
}

fn status(&self) -> bool {
self.inner.receipt.status
self.inner.status()
}

/// Return the receipt's bloom.
fn bloom(&self) -> Bloom {
self.inner.logs_bloom
}

/// Returns the cumulative gas used at this receipt.
fn cumulative_gas_used(&self) -> u128 {
self.inner.receipt.cumulative_gas_used
}

/// Return the receipt logs.
fn logs(&self) -> &[T] {
&self.inner.receipt.logs
}
Expand Down
8 changes: 6 additions & 2 deletions crates/consensus/src/receipt/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl<T> ReceiptEnvelope<T> {

/// Returns the success status of the receipt's transaction.
pub fn status(&self) -> bool {
self.as_receipt().unwrap().status
self.as_receipt().unwrap().status.coerce_status()
}

/// Returns the cumulative gas used at this receipt.
Expand Down Expand Up @@ -96,8 +96,12 @@ impl<T> ReceiptEnvelope<T> {
}

impl<T> TxReceipt<T> for ReceiptEnvelope<T> {
fn status_or_post_state(&self) -> &crate::Eip658Value {
&self.as_receipt().unwrap().status
}

fn status(&self) -> bool {
self.as_receipt().unwrap().status
self.as_receipt().unwrap().status.coerce_status()
}

/// Return the receipt's bloom.
Expand Down
34 changes: 30 additions & 4 deletions crates/consensus/src/receipt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,36 @@ pub use envelope::ReceiptEnvelope;
mod receipts;
pub use receipts::{Receipt, ReceiptWithBloom};

mod status;
pub use status::Eip658Value;

/// Receipt is the result of a transaction execution.
#[doc(alias = "TransactionReceipt")]
pub trait TxReceipt<T = Log> {
/// Returns true if the transaction was successful.
/// Returns the status or post state of the transaction.
///
/// ## Note
///
/// Use this method instead of [`TxReceipt::status`] when the transaction
/// is pre-[EIP-658].
///
/// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658
fn status_or_post_state(&self) -> &Eip658Value;

/// Returns true if the transaction was successful OR if the transaction is
/// pre-[EIP-658]. Results for transactions before [EIP-658] are not
/// reliable.
///
/// ## Note
///
/// Caution must be taken when using this method for deep-historical
/// receipts, as it may not accurately reflect the status of the
/// transaction. The transaction status is not knowable from the receipt
/// for transactions before [EIP-658].
///
/// This can be handled using [`TxReceipt::status_or_post_state`].
///
/// [EIP-658]: https://eips.ethereum.org/EIPS/eip-658
fn status(&self) -> bool;

/// Returns the bloom filter for the logs in the receipt. This operation
Expand Down Expand Up @@ -59,7 +85,7 @@ mod tests {
bytes!("0100ff"),
),
}],
status: false,
status: false.into(),
},
logs_bloom: [0; 256].into(),
});
Expand Down Expand Up @@ -91,7 +117,7 @@ mod tests {
bytes!("0100ff"),
),
}],
status: false,
status: false.into(),
},
logs_bloom: [0; 256].into(),
};
Expand All @@ -104,7 +130,7 @@ mod tests {
fn gigantic_receipt() {
let receipt = Receipt {
cumulative_gas_used: 16747627,
status: true,
status: true.into(),
logs: vec![
Log {
address: address!("4bf56695415f725e43c3e04354b604bcfb6dfb6e"),
Expand Down
78 changes: 69 additions & 9 deletions crates/consensus/src/receipt/receipts.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
use core::borrow::Borrow;

use super::TxReceipt;
use alloy_primitives::{Bloom, Log};
use crate::receipt::{Eip658Value, TxReceipt};
use alloy_primitives::{Bloom, Log, U128};
use alloy_rlp::{length_of_length, BufMut, Decodable, Encodable};
use core::borrow::Borrow;

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

/// Receipt containing result of transaction execution.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[doc(alias = "TransactionReceipt", alias = "TxReceipt")]
pub struct Receipt<T = Log> {
/// If transaction is executed successfully.
///
/// This is the `statusCode`
#[cfg_attr(feature = "serde", serde(with = "alloy_serde::quantity_bool"))]
pub status: bool,
#[cfg_attr(feature = "serde", serde(alias = "root"))]
pub status: Eip658Value,
/// Gas used
#[cfg_attr(feature = "serde", serde(with = "alloy_serde::u128_via_ruint"))]
pub cumulative_gas_used: u128,
/// Log send from contracts.
pub logs: Vec<T>,
}

#[cfg(feature = "serde")]
impl<T> serde::Serialize for Receipt<T>
where
T: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;

let mut s = serializer.serialize_struct("Receipt", 3)?;

// If the status is EIP-658, serialize the status field.
// Otherwise, serialize the root field.
let key = if self.status.is_eip658() { "status" } else { "root" };
s.serialize_field(key, &self.status)?;

s.serialize_field("cumulativeGasUsed", &U128::from(self.cumulative_gas_used))?;
s.serialize_field("logs", &self.logs)?;

s.end()
}
}

impl<T> Receipt<T>
where
T: Borrow<Log>,
Expand All @@ -47,8 +71,12 @@ impl<T> TxReceipt<T> for Receipt<T>
where
T: Borrow<Log>,
{
fn status_or_post_state(&self) -> &Eip658Value {
&self.status
}

fn status(&self) -> bool {
self.status
self.status.coerce_status()
}

fn bloom(&self) -> Bloom {
Expand Down Expand Up @@ -90,8 +118,12 @@ pub struct ReceiptWithBloom<T = Log> {
}

impl<T> TxReceipt<T> for ReceiptWithBloom<T> {
fn status_or_post_state(&self) -> &Eip658Value {
&self.receipt.status
}

fn status(&self) -> bool {
self.receipt.status
matches!(self.receipt.status, Eip658Value::Eip658(true) | Eip658Value::PostState(_))
}

fn bloom(&self) -> Bloom {
Expand Down Expand Up @@ -216,3 +248,31 @@ where
Ok(Self { receipt: Receipt::<T>::arbitrary(u)?, logs_bloom: Bloom::arbitrary(u)? })
}
}

#[cfg(test)]
mod test {
#[cfg(feature = "serde")]
#[test]
fn root_vs_status() {
let receipt = super::Receipt::<()> {
status: super::Eip658Value::Eip658(true),
cumulative_gas_used: 0,
logs: Vec::new(),
};

let json = serde_json::to_string(&receipt).unwrap();
assert_eq!(json, r#"{"status":"0x1","cumulativeGasUsed":"0x0","logs":[]}"#);

let receipt = super::Receipt::<()> {
status: super::Eip658Value::PostState(Default::default()),
cumulative_gas_used: 0,
logs: Vec::new(),
};

let json = serde_json::to_string(&receipt).unwrap();
assert_eq!(
json,
r#"{"root":"0x0000000000000000000000000000000000000000000000000000000000000000","cumulativeGasUsed":"0x0","logs":[]}"#
);
}
}
Loading

0 comments on commit 506edee

Please sign in to comment.