Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions crates/cold-mdbx/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,23 +480,28 @@ impl MdbxColdBackend {
return Ok(None);
};

let prior_cumulative_gas = index
.checked_sub(1)
.map(|prev| {
DualTableTraverse::<ColdReceipts, _>::exact_dual(
&mut tx.new_cursor::<ColdReceipts>()?,
&block,
&prev,
)
})
.transpose()?
.flatten()
.map(|r: Receipt| r.inner.cumulative_gas_used)
.unwrap_or(0);
let mut first_log_index = 0u64;
let mut prior_cumulative_gas = 0u64;
for i in 0..index {
if let Some(r) = DualTableTraverse::<ColdReceipts, _>::exact_dual(
&mut tx.new_cursor::<ColdReceipts>()?,
&block,
&i,
)? {
prior_cumulative_gas = r.inner.cumulative_gas_used;
first_log_index += r.inner.logs.len() as u64;
}
}

let meta = ConfirmationMeta::new(block, header.hash_slow(), index);
let confirmed_receipt = Confirmed::new(receipt, meta);
Ok(Some(ReceiptContext::new(header, transaction, confirmed_receipt, prior_cumulative_gas)))
Ok(Some(ReceiptContext::new(
header,
transaction,
confirmed_receipt,
prior_cumulative_gas,
first_log_index,
)))
}
}

Expand Down
65 changes: 51 additions & 14 deletions crates/cold/src/conformance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
};
use alloy::{
consensus::{Header, Receipt as AlloyReceipt, Signed, TxLegacy},
primitives::{B256, BlockNumber, Signature, TxKind, U256},
primitives::{B256, BlockNumber, Bytes, Log, Signature, TxKind, U256, address},
};
use signet_storage_types::{Receipt, TransactionSigned};

Expand Down Expand Up @@ -54,6 +54,23 @@ fn make_test_receipt() -> Receipt {
}
}

/// Create a test receipt with the given number of logs.
fn make_test_receipt_with_logs(log_count: usize, cumulative_gas: u64) -> Receipt {
let logs = (0..log_count)
.map(|_| {
Log::new_unchecked(
address!("0x0000000000000000000000000000000000000001"),
vec![],
Bytes::new(),
)
})
.collect();
Receipt {
inner: AlloyReceipt { status: true.into(), cumulative_gas_used: cumulative_gas, logs },
..Default::default()
}
}

/// Create test block data with transactions and receipts.
fn make_test_block_with_txs(block_number: BlockNumber, tx_count: usize) -> BlockData {
let header = Header { number: block_number, ..Default::default() };
Expand Down Expand Up @@ -244,33 +261,53 @@ pub async fn test_latest_block_tracking<B: ColdStorage>(backend: &B) -> ColdResu

/// Test get_receipt_with_context returns complete receipt context.
pub async fn test_get_receipt_with_context<B: ColdStorage>(backend: &B) -> ColdResult<()> {
let block = make_test_block_with_txs(700, 3);
let expected_header = block.header.clone();
let tx_hash = *block.transactions[1].tx_hash();
// Block with 3 receipts having 2, 3, and 1 logs respectively.
let header = Header { number: 700, ..Default::default() };
let transactions: Vec<_> = (0..3).map(|i| make_test_tx(700 * 100 + i)).collect();
let receipts = vec![
make_test_receipt_with_logs(2, 21000),
make_test_receipt_with_logs(3, 42000),
make_test_receipt_with_logs(1, 63000),
];
let block = BlockData::new(header.clone(), transactions.clone(), receipts, vec![], None);
let tx_hash = *transactions[1].tx_hash();

backend.append_block(block).await?;

// Lookup by block+index
let ctx = backend
// First receipt: prior_cumulative_gas=0, first_log_index=0
let first = backend
.get_receipt_with_context(ReceiptSpecifier::BlockAndIndex { block: 700, index: 0 })
.await?
.unwrap();
assert_eq!(first.header, header);
assert_eq!(first.receipt.meta().block_number(), 700);
assert_eq!(first.receipt.meta().transaction_index(), 0);
assert_eq!(first.prior_cumulative_gas, 0);
assert_eq!(first.first_log_index, 0);

// Second receipt: prior_cumulative_gas=21000, first_log_index=2
let second = backend
.get_receipt_with_context(ReceiptSpecifier::BlockAndIndex { block: 700, index: 1 })
.await?
.unwrap();
assert_eq!(ctx.header, expected_header);
assert_eq!(ctx.receipt.meta().block_number(), 700);
assert_eq!(ctx.receipt.meta().transaction_index(), 1);
assert_eq!(second.receipt.meta().transaction_index(), 1);
assert_eq!(second.prior_cumulative_gas, 21000);
assert_eq!(second.first_log_index, 2);

// prior_cumulative_gas should equal receipt[0].cumulative_gas_used
let first = backend
.get_receipt_with_context(ReceiptSpecifier::BlockAndIndex { block: 700, index: 0 })
// Third receipt: prior_cumulative_gas=42000, first_log_index=5 (2+3)
let third = backend
.get_receipt_with_context(ReceiptSpecifier::BlockAndIndex { block: 700, index: 2 })
.await?
.unwrap();
assert_eq!(first.prior_cumulative_gas, 0);
assert_eq!(ctx.prior_cumulative_gas, first.receipt.inner().inner.cumulative_gas_used);
assert_eq!(third.receipt.meta().transaction_index(), 2);
assert_eq!(third.prior_cumulative_gas, 42000);
assert_eq!(third.first_log_index, 5);

// Lookup by tx hash
let by_hash =
backend.get_receipt_with_context(ReceiptSpecifier::TxHash(tx_hash)).await?.unwrap();
assert_eq!(by_hash.receipt.meta().transaction_index(), 1);
assert_eq!(by_hash.first_log_index, 2);

// Non-existent returns None
assert!(
Expand Down
35 changes: 22 additions & 13 deletions crates/cold/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ impl BlockData {
/// All data needed to build a complete RPC receipt response.
///
/// Bundles a [`Confirmed`] receipt with its transaction, block header,
/// and the prior cumulative gas (needed to compute per-tx `gas_used`).
/// the prior cumulative gas (needed to compute per-tx `gas_used`),
/// and the index of this receipt's first log among all logs in the block
/// (needed for `logIndex` in RPC responses).
#[derive(Debug, Clone)]
pub struct ReceiptContext {
/// The block header.
Expand All @@ -63,6 +65,9 @@ pub struct ReceiptContext {
/// Cumulative gas used by all preceding transactions in the block.
/// Zero for the first transaction.
pub prior_cumulative_gas: u64,
/// Index of this receipt's first log among all logs in the block.
/// Equal to the sum of log counts from all preceding receipts.
pub first_log_index: u64,
}

impl ReceiptContext {
Expand All @@ -72,8 +77,9 @@ impl ReceiptContext {
transaction: TransactionSigned,
receipt: Confirmed<Receipt>,
prior_cumulative_gas: u64,
first_log_index: u64,
) -> Self {
Self { header, transaction, receipt, prior_cumulative_gas }
Self { header, transaction, receipt, prior_cumulative_gas, first_log_index }
}
}

Expand Down Expand Up @@ -197,7 +203,8 @@ pub trait ColdStorage: Send + Sync + 'static {
/// Get a receipt with all context needed for RPC responses.
///
/// Returns the receipt, its transaction, the block header, confirmation
/// metadata, and the cumulative gas used by preceding transactions.
/// metadata, the cumulative gas used by preceding transactions, and the
/// index of this receipt's first log among all logs in the block.
/// Returns `None` if the receipt does not exist.
///
/// The default implementation composes existing trait methods. Backends
Expand All @@ -223,16 +230,18 @@ pub trait ColdStorage: Send + Sync + 'static {
return Ok(None);
};

let prior_cumulative_gas = if index > 0 {
self.get_receipt(ReceiptSpecifier::BlockAndIndex { block, index: index - 1 })
.await?
.map(|r| r.into_inner().inner.cumulative_gas_used)
.unwrap_or(0)
} else {
0
};

Ok(Some(ReceiptContext::new(header, tx.into_inner(), receipt, prior_cumulative_gas)))
let receipts = self.get_receipts_in_block(block).await?;
let prior = &receipts[..index as usize];
let prior_cumulative_gas = prior.last().map_or(0, |r| r.inner.cumulative_gas_used);
let first_log_index = prior.iter().map(|r| r.inner.logs.len() as u64).sum();

Ok(Some(ReceiptContext::new(
header,
tx.into_inner(),
receipt,
prior_cumulative_gas,
first_log_index,
)))
}
}

Expand Down