Skip to content

Commit 6224e82

Browse files
authored
refactor: replace custom MPT and triehash with alloy-trie (#506)
* replace custom proof impl and triehash with alloy-trie * fix some clippy warnings
1 parent 14759c4 commit 6224e82

File tree

9 files changed

+161
-583
lines changed

9 files changed

+161
-583
lines changed

Cargo.lock

Lines changed: 56 additions & 292 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,16 @@ alloy = { version = "0.9.1", features = [
4343
"ssz",
4444
"json-rpc",
4545
"signers",
46-
]}
46+
] }
47+
alloy-trie = "0.7.8"
4748
op-alloy-rpc-types = "0.9.0"
4849
revm = { version = "18.0.0", default-features = false, features = [
4950
"std",
5051
"serde",
5152
"optional_block_gas_limit",
5253
"optional_eip3607",
5354
"optional_no_base_fee",
54-
]}
55-
triehash-ethereum = { git = "https://github.com/openethereum/parity-ethereum", rev = "55c90d4016505317034e3e98f699af07f5404b63" }
55+
] }
5656

5757
# async/futures
5858
async-trait = "0.1.57"

core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ edition = "2021"
66
[dependencies]
77
# execution
88
alloy.workspace = true
9+
alloy-trie.workspace = true
910
revm.workspace = true
10-
triehash-ethereum.workspace = true
1111

1212
# async/futures
1313
futures.workspace = true

core/src/execution/mod.rs

Lines changed: 51 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,31 @@ use alloy::consensus::BlockHeader;
44
use alloy::network::primitives::HeaderResponse;
55
use alloy::network::{BlockResponse, ReceiptResponse};
66
use alloy::primitives::{keccak256, Address, B256, U256};
7-
use alloy::rlp::encode;
7+
use alloy::rlp;
88
use alloy::rpc::types::{BlockTransactions, Filter, FilterChanges, Log};
9+
use alloy_trie::{
10+
proof::verify_proof as mpt_verify_proof,
11+
root::ordered_trie_root_with_encoder,
12+
{Nibbles, TrieAccount},
13+
};
914
use constants::{BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS};
1015
use eyre::Result;
1116
use futures::future::try_join_all;
1217
use revm::primitives::KECCAK_EMPTY;
1318
use tracing::warn;
14-
use triehash_ethereum::ordered_trie_root;
1519

1620
use crate::network_spec::NetworkSpec;
1721
use crate::types::BlockTag;
1822

1923
use self::constants::MAX_SUPPORTED_LOGS_NUMBER;
2024
use self::errors::ExecutionError;
21-
use self::proof::{encode_account, verify_proof};
2225
use self::rpc::ExecutionRpc;
2326
use self::state::{FilterType, State};
2427
use self::types::Account;
2528

2629
pub mod constants;
2730
pub mod errors;
2831
pub mod evm;
29-
pub mod proof;
3032
pub mod rpc;
3133
pub mod state;
3234
pub mod types;
@@ -69,48 +71,49 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
6971
.get_proof(address, slots, block.header().number().into())
7072
.await?;
7173

72-
let account_path = keccak256(address).to_vec();
73-
let account_encoded = encode_account(&proof);
74+
// Verify the account proof
75+
let account_key = Nibbles::unpack(keccak256(proof.address));
76+
let account = TrieAccount {
77+
nonce: proof.nonce,
78+
balance: proof.balance,
79+
storage_root: proof.storage_hash,
80+
code_hash: proof.code_hash,
81+
};
82+
let account_encoded = rlp::encode(account);
7483

75-
let is_valid = verify_proof(
84+
mpt_verify_proof(
85+
block.header().state_root(),
86+
account_key,
87+
account_encoded.into(),
7688
&proof.account_proof,
77-
block.header().state_root().as_slice(),
78-
&account_path,
79-
&account_encoded,
80-
);
81-
82-
if !is_valid {
83-
return Err(ExecutionError::InvalidAccountProof(address).into());
84-
}
89+
)
90+
.map_err(|_| ExecutionError::InvalidAccountProof(address))?;
8591

92+
// Verify the storage proofs, collecting the slot values
8693
let mut slot_map = HashMap::new();
8794

8895
for storage_proof in proof.storage_proof {
8996
let key = storage_proof.key.as_b256();
9097
let key_hash = keccak256(key);
91-
let value = encode(storage_proof.value);
98+
let key_nibbles = Nibbles::unpack(key_hash);
99+
let encoded_value = rlp::encode(storage_proof.value);
92100

93-
let is_valid = verify_proof(
101+
mpt_verify_proof(
102+
proof.storage_hash,
103+
key_nibbles,
104+
encoded_value.into(),
94105
&storage_proof.proof,
95-
proof.storage_hash.as_slice(),
96-
key_hash.as_slice(),
97-
&value,
98-
);
99-
100-
if !is_valid {
101-
return Err(ExecutionError::InvalidStorageProof(address, key).into());
102-
}
106+
)
107+
.map_err(|_| ExecutionError::InvalidStorageProof(address, key))?;
103108

104109
slot_map.insert(key, storage_proof.value);
105110
}
106111

112+
// Verify the code hash
107113
let code = if proof.code_hash == KECCAK_EMPTY || proof.code_hash == B256::ZERO {
108114
Vec::new()
109115
} else {
110-
let code = self
111-
.rpc
112-
.get_code(address, block.header().number().into())
113-
.await?;
116+
let code = self.rpc.get_code(address, block.header().number()).await?;
114117
let code_hash = keccak256(&code);
115118

116119
if proof.code_hash != code_hash {
@@ -256,8 +259,7 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
256259
.ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?;
257260

258261
let receipts_encoded = receipts.iter().map(N::encode_receipt).collect::<Vec<_>>();
259-
let expected_receipt_root = ordered_trie_root(receipts_encoded.clone());
260-
let expected_receipt_root = B256::from_slice(&expected_receipt_root.to_fixed_bytes());
262+
let expected_receipt_root = ordered_trie_root(&receipts_encoded);
261263

262264
if expected_receipt_root != block.header().receipts_root()
263265
// Note: Some RPC providers return different response in `eth_getTransactionReceipt` vs `eth_getBlockReceipts`
@@ -286,7 +288,7 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
286288
return Ok(None);
287289
};
288290

289-
let tag = BlockTag::Number(block.header().number().into());
291+
let tag = BlockTag::Number(block.header().number());
290292

291293
let receipts = self
292294
.rpc
@@ -295,8 +297,7 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
295297
.ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?;
296298

297299
let receipts_encoded = receipts.iter().map(N::encode_receipt).collect::<Vec<_>>();
298-
let expected_receipt_root = ordered_trie_root(receipts_encoded);
299-
let expected_receipt_root = B256::from_slice(&expected_receipt_root.to_fixed_bytes());
300+
let expected_receipt_root = ordered_trie_root(&receipts_encoded);
300301

301302
if expected_receipt_root != block.header().receipts_root() {
302303
return Err(ExecutionError::BlockReceiptsRootMismatch(tag).into());
@@ -369,7 +370,7 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
369370
self.state
370371
.push_filter(
371372
filter_id,
372-
FilterType::NewBlock(blocks.last().unwrap().header().number().into()),
373+
FilterType::NewBlock(blocks.last().unwrap().header().number()),
373374
)
374375
.await;
375376
}
@@ -476,7 +477,10 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
476477
None
477478
} else {
478479
let tx_hash = logs[0].transaction_hash.unwrap();
479-
let encoded_logs = logs.iter().map(|l| encode(&l.inner)).collect::<Vec<_>>();
480+
let encoded_logs = logs
481+
.iter()
482+
.map(|l| rlp::encode(&l.inner))
483+
.collect::<Vec<_>>();
480484
Some((tx_hash, encoded_logs))
481485
}
482486
})
@@ -486,7 +490,7 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
486490
// Check if the receipt contains the desired log
487491
// Encoding logs for comparison
488492
let tx_hash = log.transaction_hash.unwrap();
489-
let log_encoded = encode(&log.inner);
493+
let log_encoded = rlp::encode(&log.inner);
490494
let receipt_logs_encoded = receipts_logs_encoded.get(&tx_hash).unwrap();
491495

492496
if !receipt_logs_encoded.contains(&log_encoded) {
@@ -500,3 +504,13 @@ impl<N: NetworkSpec, R: ExecutionRpc<N>> ExecutionClient<N, R> {
500504
Ok(())
501505
}
502506
}
507+
508+
/// Compute a trie root of a collection of encoded items.
509+
/// Ref: https://github.com/alloy-rs/trie/blob/main/src/root.rs.
510+
fn ordered_trie_root(items: &[Vec<u8>]) -> B256 {
511+
fn noop_encoder(item: &Vec<u8>, buffer: &mut Vec<u8>) {
512+
buffer.extend_from_slice(item);
513+
}
514+
515+
ordered_trie_root_with_encoder(items, noop_encoder)
516+
}

0 commit comments

Comments
 (0)