Skip to content

Commit

Permalink
Changed receipt proof query to make fewer calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Zyouell committed Feb 5, 2025
1 parent e130bd9 commit 143ffa6
Showing 1 changed file with 1 addition and 174 deletions.
175 changes: 1 addition & 174 deletions mp2-common/src/eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,11 +948,7 @@ mod test {
let mut block = BlockUtil::fetch(&provider, bna).await?;
// check if we compute the RLP correctly now
block.check()?;
let mut be = tryethers::BlockData::fetch(bn, url).await?;
be.check()?;
let er = be.receipts_trie.root_hash()?;
let ar = block.receipts_trie.root_hash()?;
assert_eq!(er, ar);

// dissect one receipt entry in the trie
let tx_receipt = block.txs.first().unwrap();
// https://sepolia.etherscan.io/tx/0x9bef12fafd3962b0e0d66b738445d6ea2c1f3daabe10c889bd1916acc75d698b#eventlog
Expand Down Expand Up @@ -1449,173 +1445,4 @@ mod test {
rlp.append(inner);
}
}
// for compatibility check with alloy
mod tryethers {

use std::sync::Arc;

use anyhow::Result;
use eth_trie::{EthTrie, MemoryDB, Trie};
use ethers::{
providers::{Http, Middleware, Provider},
types::{BlockId, Bytes, Transaction, TransactionReceipt, U64},
};
use rlp::{Encodable, RlpStream};

/// A wrapper around a transaction and its receipt. The receipt is used to filter
/// bad transactions, so we only compute over valid transactions.
pub struct TxAndReceipt(Transaction, TransactionReceipt);

impl TxAndReceipt {
pub fn tx(&self) -> &Transaction {
&self.0
}
pub fn receipt(&self) -> &TransactionReceipt {
&self.1
}
pub fn tx_rlp(&self) -> Bytes {
self.0.rlp()
}
// TODO: this should be upstreamed to ethers-rs
pub fn receipt_rlp(&self) -> Bytes {
let tx_type = self.tx().transaction_type;
let mut rlp = RlpStream::new();
rlp.begin_unbounded_list();
match &self.1.status {
Some(s) if s.as_u32() == 1 => rlp.append(s),
_ => rlp.append_empty_data(),
};
rlp.append(&self.1.cumulative_gas_used)
.append(&self.1.logs_bloom)
.append_list(&self.1.logs);

rlp.finalize_unbounded_list();
let rlp_bytes: Bytes = rlp.out().freeze().into();
let mut encoded = vec![];
match tx_type {
// EIP-2930 (0x01)
Some(x) if x == U64::from(0x1) => {
encoded.extend_from_slice(&[0x1]);
encoded.extend_from_slice(rlp_bytes.as_ref());
encoded.into()
}
// EIP-1559 (0x02)
Some(x) if x == U64::from(0x2) => {
encoded.extend_from_slice(&[0x2]);
encoded.extend_from_slice(rlp_bytes.as_ref());
encoded.into()
}
_ => rlp_bytes,
}
}
}
/// Structure containing the block header and its transactions / receipts. Amongst other things,
/// it is used to create a proof of inclusion for any transaction inside this block.
pub struct BlockData {
pub block: ethers::types::Block<Transaction>,
// TODO: add generics later - this may be re-used amongst different workers
pub tx_trie: EthTrie<MemoryDB>,
pub receipts_trie: EthTrie<MemoryDB>,
}

impl BlockData {
pub async fn fetch<T: Into<BlockId> + Send + Sync>(
blockid: T,
url: String,
) -> Result<Self> {
let provider =
Provider::<Http>::try_from(url).expect("could not instantiate HTTP Provider");
Self::fetch_from(&provider, blockid).await
}
pub async fn fetch_from<T: Into<BlockId> + Send + Sync>(
provider: &Provider<Http>,
blockid: T,
) -> Result<Self> {
let block = provider
.get_block_with_txs(blockid)
.await?
.expect("should have been a block");
let receipts = provider
.get_block_receipts(
block
.number
.ok_or(anyhow::anyhow!("Couldn't unwrap block number"))?,
)
.await
.map_err(|e| {
anyhow::anyhow!("Couldn't get ethers block receipts with error: {:?}", e)
})?;

let tx_with_receipt = block
.transactions
.clone()
.into_iter()
.map(|tx| {
let tx_hash = tx.hash();
let r = receipts
.iter()
.find(|r| r.transaction_hash == tx_hash)
.expect("RPC sending invalid data");
// TODO remove cloning
TxAndReceipt(tx, r.clone())
})
.collect::<Vec<_>>();

// check transaction root
let memdb = Arc::new(MemoryDB::new(true));
let mut tx_trie = EthTrie::new(Arc::clone(&memdb));
for tr in tx_with_receipt.iter() {
tx_trie
.insert(&tr.receipt().transaction_index.rlp_bytes(), &tr.tx_rlp())
.expect("can't insert tx");
}

// check receipt root
let memdb = Arc::new(MemoryDB::new(true));
let mut receipts_trie = EthTrie::new(Arc::clone(&memdb));
for tr in tx_with_receipt.iter() {
if tr.tx().transaction_index.unwrap() == U64::from(0) {
println!(
"Ethers: Index {} -> {:?}",
tr.tx().transaction_index.unwrap(),
tr.receipt_rlp().to_vec()
);
}
receipts_trie
.insert(
&tr.receipt().transaction_index.rlp_bytes(),
// TODO: make getter value for rlp encoding
&tr.receipt_rlp(),
)
.expect("can't insert tx");
}
let computed = tx_trie.root_hash().expect("root hash problem");
let expected = block.transactions_root;
assert_eq!(expected, computed);

let computed = receipts_trie.root_hash().expect("root hash problem");
let expected = block.receipts_root;
assert_eq!(expected, computed);

Ok(BlockData {
block,
tx_trie,
receipts_trie,
})
}

// recompute the receipts trie by first converting all receipts form RPC type to consensus type
// since in Alloy these are two different types and RLP functions are only implemented for
// consensus ones.
pub fn check(&mut self) -> Result<()> {
let computed = self.receipts_trie.root_hash()?;
let tx_computed = self.tx_trie.root_hash()?;
let expected = self.block.receipts_root;
let tx_expected = self.block.transactions_root;
assert_eq!(expected.0, computed.0);
assert_eq!(tx_expected.0, tx_computed.0);
Ok(())
}
}
}
}

0 comments on commit 143ffa6

Please sign in to comment.