From 0113bcb72511535dd1edfbbd4e0e43ff9017a581 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Fri, 11 Jul 2025 17:35:36 +0530 Subject: [PATCH 1/9] feat: added get_blob_by_hash --- crates/anvil/core/src/eth/mod.rs | 4 +++ crates/anvil/src/eth/api.rs | 40 +++++++++++++++++++++++++++++ crates/anvil/tests/it/eip4844.rs | 43 ++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index 442ac255ac50c..b8b8e43b5b72b 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -178,6 +178,10 @@ pub enum EthRequest { #[serde(rename = "eth_getTransactionByHash", with = "sequence")] EthGetTransactionByHash(TxHash), + /// Method to retrieve the blob by its hash. + #[serde(rename = "anvil_getBlobByHash", with = "sequence")] + GetBlobByHash(TxHash), + #[serde(rename = "eth_getTransactionByBlockHashAndIndex")] EthGetTransactionByBlockHashAndIndex(TxHash, Index), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index bd33d4f6060e9..35b16fabcb8fa 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -279,6 +279,9 @@ impl EthApi { EthRequest::EthGetRawTransactionByHash(hash) => { self.raw_transaction(hash).await.to_rpc_result() } + EthRequest::GetBlobByHash(hash) => { + self.anvil_get_blob_by_hash(hash).await.to_rpc_result() + } EthRequest::EthGetRawTransactionByBlockHashAndIndex(hash, index) => { self.raw_transaction_by_block_hash_and_index(hash, index).await.to_rpc_result() } @@ -1312,6 +1315,43 @@ impl EthApi { .map(U256::from) } + /// Handler for RPC call: `anvil_getBlobByHash` + pub async fn anvil_get_blob_by_hash( + &self, + hash: B256, + ) -> Result> { + node_info!("anvil_getBlobByHash"); + let latest_block = self.backend.best_number(); + println!("Searching for blob with hash: {hash} in blocks 0..={latest_block}"); + for i in 0..=latest_block { + if let Ok(Some(block)) = self.backend.block_by_number(i.into()).await { + // println!("BLOCK #{i}: {:#?}", block); + println!("Transactions in block {i}: {:?}", block.transactions()); + + for tx in block.into_transactions_iter() { + println!("Tx: {:?}", tx); + println!("Rpc Transaction got in block {:?}", i); + let typed_tx = TypedTransaction::try_from(tx).unwrap(); + println!("Typed Transaction got in block {:?}", i); + if let TypedTransaction::EIP4844(signed) = typed_tx { + println!("Got Signed tx"); + if let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() { + for item in sidecar_tx.sidecar.clone() { + let versioned_hash = B256::from(item.to_kzg_versioned_hash()); + println!("Versioned Hash: {:?}", versioned_hash); + if versioned_hash == hash { + return Ok(Some(sidecar_tx)); + } + } + } + } + } + } + } + + Ok(None) + } + /// Get transaction by its hash. /// /// This will check the storage for a matching transaction, if no transaction exists in storage diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 3d512dbc333ff..f75dba38b415c 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -346,3 +346,46 @@ async fn can_bypass_sidecar_requirement() { assert_eq!(tx.inner.ty(), 3); } + +#[tokio::test(flavor = "multi_thread")] +async fn can_get_blobs_by_hash() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); + let (api, handle) = spawn(node_config).await; + + let wallets = handle.dev_wallets().collect::>(); + let from = wallets[0].address(); + let to = wallets[1].address(); + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Hello World"); + + let sidecar = sidecar.build().unwrap(); + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar.clone()) + .value(U256::from(5)); + + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + + let hash = sidecar.versioned_hash_for_blob(0).unwrap(); + println!("Blob hash: {:?}", hash); + println!("Transaction hash: {:?}", receipt.transaction_hash); + api.anvil_set_auto_mine(true).await.unwrap(); + let blob = api.anvil_get_blob_by_hash(hash).await.unwrap().unwrap(); + println!("Blob: {:?}", blob); + + assert_eq!(receipt.blob_gas_used, Some(131072)); + assert_eq!(receipt.blob_gas_price, Some(0x1)); // 1 wei +} From b90f428f515e2df4c43ed19b7866deb025919860 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Fri, 11 Jul 2025 18:47:41 +0530 Subject: [PATCH 2/9] use correct get_full_block_fn --- crates/anvil/core/src/eth/mod.rs | 4 ++- crates/anvil/src/eth/api.rs | 47 ++++++++++++++++++++++-------- crates/anvil/tests/it/eip4844.rs | 50 ++++++++++++++++++++++++++------ 3 files changed, 79 insertions(+), 22 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index b8b8e43b5b72b..e24f5bf556bfd 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -178,10 +178,12 @@ pub enum EthRequest { #[serde(rename = "eth_getTransactionByHash", with = "sequence")] EthGetTransactionByHash(TxHash), - /// Method to retrieve the blob by its hash. #[serde(rename = "anvil_getBlobByHash", with = "sequence")] GetBlobByHash(TxHash), + #[serde(rename = "anvil_getBlobByTransactionHash", with = "sequence")] + GetBlobByTransactionHash(TxHash), + #[serde(rename = "eth_getTransactionByBlockHashAndIndex")] EthGetTransactionByBlockHashAndIndex(TxHash, Index), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 35b16fabcb8fa..fe3dfda5e798c 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -280,7 +280,10 @@ impl EthApi { self.raw_transaction(hash).await.to_rpc_result() } EthRequest::GetBlobByHash(hash) => { - self.anvil_get_blob_by_hash(hash).await.to_rpc_result() + self.anvil_get_blob_by_versioned_hash(hash).await.to_rpc_result() + } + EthRequest::GetBlobByTransactionHash(hash) => { + self.anvil_get_blob_by_tx_hash(hash).await.to_rpc_result() } EthRequest::EthGetRawTransactionByBlockHashAndIndex(hash, index) => { self.raw_transaction_by_block_hash_and_index(hash, index).await.to_rpc_result() @@ -1316,29 +1319,22 @@ impl EthApi { } /// Handler for RPC call: `anvil_getBlobByHash` - pub async fn anvil_get_blob_by_hash( + pub async fn anvil_get_blob_by_versioned_hash( &self, hash: B256, ) -> Result> { node_info!("anvil_getBlobByHash"); let latest_block = self.backend.best_number(); - println!("Searching for blob with hash: {hash} in blocks 0..={latest_block}"); for i in 0..=latest_block { - if let Ok(Some(block)) = self.backend.block_by_number(i.into()).await { - // println!("BLOCK #{i}: {:#?}", block); - println!("Transactions in block {i}: {:?}", block.transactions()); - + if let Some(block) = self.backend.get_full_block(i) { for tx in block.into_transactions_iter() { - println!("Tx: {:?}", tx); - println!("Rpc Transaction got in block {:?}", i); let typed_tx = TypedTransaction::try_from(tx).unwrap(); - println!("Typed Transaction got in block {:?}", i); + if let TypedTransaction::EIP4844(signed) = typed_tx { - println!("Got Signed tx"); if let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() { for item in sidecar_tx.sidecar.clone() { let versioned_hash = B256::from(item.to_kzg_versioned_hash()); - println!("Versioned Hash: {:?}", versioned_hash); + if versioned_hash == hash { return Ok(Some(sidecar_tx)); } @@ -1352,6 +1348,33 @@ impl EthApi { Ok(None) } + /// Handler for RPC call: `anvil_getBlobByTransactionHash` + pub async fn anvil_get_blob_by_tx_hash( + &self, + hash: B256, + ) -> Result> { + node_info!("anvil_getBlobByTransactionHash"); + let latest_block = self.backend.best_number(); + for i in 0..=latest_block { + if let Some(block) = self.backend.get_full_block(i) { + for tx in block.into_transactions_iter() { + let typed_tx = TypedTransaction::try_from(tx).unwrap(); + + if let TypedTransaction::EIP4844(signed) = typed_tx { + if *signed.hash() == hash { + if let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() + { + return Ok(Some(sidecar_tx)); + } + } + } + } + } + } + + Ok(None) + } + /// Get transaction by its hash. /// /// This will check the storage for a matching transaction, if no transaction exists in storage diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index f75dba38b415c..51efbb60bb3ce 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -348,7 +348,7 @@ async fn can_bypass_sidecar_requirement() { } #[tokio::test(flavor = "multi_thread")] -async fn can_get_blobs_by_hash() { +async fn can_get_blobs_by_versioned_hash() { let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); let (api, handle) = spawn(node_config).await; @@ -377,15 +377,47 @@ async fn can_get_blobs_by_hash() { tx.populate_blob_hashes(); - let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let _receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let hash = sidecar.versioned_hash_for_blob(0).unwrap(); - println!("Blob hash: {:?}", hash); - println!("Transaction hash: {:?}", receipt.transaction_hash); - api.anvil_set_auto_mine(true).await.unwrap(); - let blob = api.anvil_get_blob_by_hash(hash).await.unwrap().unwrap(); - println!("Blob: {:?}", blob); + // api.anvil_set_auto_mine(true).await.unwrap(); + let blob = api.anvil_get_blob_by_versioned_hash(hash).await.unwrap().unwrap(); + assert_eq!(blob.sidecar, sidecar); +} - assert_eq!(receipt.blob_gas_used, Some(131072)); - assert_eq!(receipt.blob_gas_price, Some(0x1)); // 1 wei +#[tokio::test(flavor = "multi_thread")] +async fn can_get_blobs_by_tx_hash() { + let node_config = NodeConfig::test().with_hardfork(Some(EthereumHardfork::Prague.into())); + let (api, handle) = spawn(node_config).await; + + let wallets = handle.dev_wallets().collect::>(); + let from = wallets[0].address(); + let to = wallets[1].address(); + let provider = http_provider(&handle.http_endpoint()); + + let eip1559_est = provider.estimate_eip1559_fees().await.unwrap(); + let gas_price = provider.get_gas_price().await.unwrap(); + + let sidecar: SidecarBuilder = SidecarBuilder::from_slice(b"Hello World"); + + let sidecar = sidecar.build().unwrap(); + let tx = TransactionRequest::default() + .with_from(from) + .with_to(to) + .with_nonce(0) + .with_max_fee_per_blob_gas(gas_price + 1) + .with_max_fee_per_gas(eip1559_est.max_fee_per_gas) + .with_max_priority_fee_per_gas(eip1559_est.max_priority_fee_per_gas) + .with_blob_sidecar(sidecar.clone()) + .value(U256::from(5)); + + let mut tx = WithOtherFields::new(tx); + + tx.populate_blob_hashes(); + + let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); + let hash = receipt.transaction_hash; + api.anvil_set_auto_mine(true).await.unwrap(); + let blob = api.anvil_get_blob_by_tx_hash(hash).await.unwrap().unwrap(); + assert_eq!(blob.sidecar, sidecar); } From 70bb473f30dcd9b82ea33a9d95daab0e46d653dc Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Fri, 11 Jul 2025 18:54:30 +0530 Subject: [PATCH 3/9] clippy --- crates/anvil/src/eth/api.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index fe3dfda5e798c..e0bd1be3a83c4 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1330,14 +1330,14 @@ impl EthApi { for tx in block.into_transactions_iter() { let typed_tx = TypedTransaction::try_from(tx).unwrap(); - if let TypedTransaction::EIP4844(signed) = typed_tx { - if let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() { - for item in sidecar_tx.sidecar.clone() { - let versioned_hash = B256::from(item.to_kzg_versioned_hash()); + if let TypedTransaction::EIP4844(signed) = typed_tx + && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() + { + for item in sidecar_tx.sidecar.clone() { + let versioned_hash = B256::from(item.to_kzg_versioned_hash()); - if versioned_hash == hash { - return Ok(Some(sidecar_tx)); - } + if versioned_hash == hash { + return Ok(Some(sidecar_tx)); } } } @@ -1361,11 +1361,10 @@ impl EthApi { let typed_tx = TypedTransaction::try_from(tx).unwrap(); if let TypedTransaction::EIP4844(signed) = typed_tx { - if *signed.hash() == hash { - if let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() - { - return Ok(Some(sidecar_tx)); - } + if *signed.hash() == hash + && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() + { + return Ok(Some(sidecar_tx)); } } } From a44a7904bfc201fe1405c805486a665342728c72 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Fri, 11 Jul 2025 19:02:39 +0530 Subject: [PATCH 4/9] clippy --- crates/anvil/src/eth/api.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index e0bd1be3a83c4..52e3f499fa054 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1360,12 +1360,11 @@ impl EthApi { for tx in block.into_transactions_iter() { let typed_tx = TypedTransaction::try_from(tx).unwrap(); - if let TypedTransaction::EIP4844(signed) = typed_tx { - if *signed.hash() == hash - && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() - { - return Ok(Some(sidecar_tx)); - } + if let TypedTransaction::EIP4844(signed) = typed_tx + && *signed.hash() == hash + && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() + { + return Ok(Some(sidecar_tx)); } } } From 215862278b1c03502dc23f43dbf4eee3f661bae2 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Sat, 12 Jul 2025 15:42:06 +0530 Subject: [PATCH 5/9] refactoring and moving to backend --- crates/anvil/core/src/eth/mod.rs | 6 ++- crates/anvil/core/src/eth/transaction/mod.rs | 10 ++++ crates/anvil/src/eth/api.rs | 49 +++----------------- crates/anvil/src/eth/backend/mem/mod.rs | 35 +++++++++++++- crates/anvil/tests/it/eip4844.rs | 4 +- 5 files changed, 56 insertions(+), 48 deletions(-) diff --git a/crates/anvil/core/src/eth/mod.rs b/crates/anvil/core/src/eth/mod.rs index e24f5bf556bfd..366c648af301c 100644 --- a/crates/anvil/core/src/eth/mod.rs +++ b/crates/anvil/core/src/eth/mod.rs @@ -178,10 +178,12 @@ pub enum EthRequest { #[serde(rename = "eth_getTransactionByHash", with = "sequence")] EthGetTransactionByHash(TxHash), + /// Returns the blob for a given blob versioned hash. #[serde(rename = "anvil_getBlobByHash", with = "sequence")] - GetBlobByHash(TxHash), + GetBlobByHash(B256), - #[serde(rename = "anvil_getBlobByTransactionHash", with = "sequence")] + /// Returns the blobs for a given transaction hash. + #[serde(rename = "anvil_getBlobsByTransactionHash", with = "sequence")] GetBlobByTransactionHash(TxHash), #[serde(rename = "eth_getTransactionByBlockHashAndIndex")] diff --git a/crates/anvil/core/src/eth/transaction/mod.rs b/crates/anvil/core/src/eth/transaction/mod.rs index f4c2f34a7d930..a85d08e38c373 100644 --- a/crates/anvil/core/src/eth/transaction/mod.rs +++ b/crates/anvil/core/src/eth/transaction/mod.rs @@ -723,6 +723,16 @@ impl TypedTransaction { } } + pub fn sidecar(&self) -> Option<&TxEip4844WithSidecar> { + match self { + Self::EIP4844(signed_variant) => match signed_variant.tx() { + TxEip4844Variant::TxEip4844WithSidecar(with_sidecar) => Some(with_sidecar), + _ => None, + }, + _ => None, + } + } + pub fn max_fee_per_blob_gas(&self) -> Option { match self { Self::EIP4844(tx) => Some(tx.tx().tx().max_fee_per_blob_gas), diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 52e3f499fa054..f836d1ca1bec3 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -280,10 +280,10 @@ impl EthApi { self.raw_transaction(hash).await.to_rpc_result() } EthRequest::GetBlobByHash(hash) => { - self.anvil_get_blob_by_versioned_hash(hash).await.to_rpc_result() + self.anvil_get_blob_by_versioned_hash(hash).to_rpc_result() } EthRequest::GetBlobByTransactionHash(hash) => { - self.anvil_get_blob_by_tx_hash(hash).await.to_rpc_result() + self.anvil_get_blob_by_tx_hash(hash).to_rpc_result() } EthRequest::EthGetRawTransactionByBlockHashAndIndex(hash, index) => { self.raw_transaction_by_block_hash_and_index(hash, index).await.to_rpc_result() @@ -1319,58 +1319,21 @@ impl EthApi { } /// Handler for RPC call: `anvil_getBlobByHash` - pub async fn anvil_get_blob_by_versioned_hash( + pub fn anvil_get_blob_by_versioned_hash( &self, hash: B256, ) -> Result> { node_info!("anvil_getBlobByHash"); - let latest_block = self.backend.best_number(); - for i in 0..=latest_block { - if let Some(block) = self.backend.get_full_block(i) { - for tx in block.into_transactions_iter() { - let typed_tx = TypedTransaction::try_from(tx).unwrap(); - - if let TypedTransaction::EIP4844(signed) = typed_tx - && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() - { - for item in sidecar_tx.sidecar.clone() { - let versioned_hash = B256::from(item.to_kzg_versioned_hash()); - - if versioned_hash == hash { - return Ok(Some(sidecar_tx)); - } - } - } - } - } - } - - Ok(None) + Ok(self.backend.get_blob_by_versioned_hash(hash)?) } /// Handler for RPC call: `anvil_getBlobByTransactionHash` - pub async fn anvil_get_blob_by_tx_hash( + pub fn anvil_get_blob_by_tx_hash( &self, hash: B256, ) -> Result> { node_info!("anvil_getBlobByTransactionHash"); - let latest_block = self.backend.best_number(); - for i in 0..=latest_block { - if let Some(block) = self.backend.get_full_block(i) { - for tx in block.into_transactions_iter() { - let typed_tx = TypedTransaction::try_from(tx).unwrap(); - - if let TypedTransaction::EIP4844(signed) = typed_tx - && *signed.hash() == hash - && let Ok(sidecar_tx) = signed.tx().clone().try_into_4844_with_sidecar() - { - return Ok(Some(sidecar_tx)); - } - } - } - } - - Ok(None) + Ok(self.backend.get_blob_by_tx_hash(hash)?) } /// Get transaction by its hash. diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 5b775838d625a..66bb6ea86f70c 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -35,7 +35,7 @@ use crate::{ use alloy_chains::NamedChain; use alloy_consensus::{ Account, BlockHeader, EnvKzgSettings, Header, Receipt, ReceiptWithBloom, Signed, - Transaction as TransactionTrait, TxEnvelope, + Transaction as TransactionTrait, TxEip4844WithSidecar, TxEnvelope, proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; @@ -2918,6 +2918,39 @@ impl Backend { )) } + pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result> { + let storage = self.blockchain.storage.read(); + for (_, block) in &storage.blocks { + for tx in &block.transactions { + let typed_tx = tx.as_ref(); + if *typed_tx.hash() == hash { + if let Some(sidecar) = typed_tx.sidecar() { + return Ok(Some(sidecar.clone())); + } + } + } + } + Ok(None) + } + + pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result> { + let storage = self.blockchain.storage.read(); + for (_, block) in &storage.blocks { + for tx in &block.transactions { + let typed_tx = tx.as_ref(); + if let Some(sidecar) = typed_tx.sidecar() { + for blob in sidecar.sidecar.clone() { + let versioned_hash = B256::from(blob.to_kzg_versioned_hash()); + if versioned_hash == hash { + return Ok(Some(sidecar.clone())); + } + } + } + } + } + Ok(None) + } + /// Prove an account's existence or nonexistence in the state trie. /// /// Returns a merkle proof of the account's trie node, `account_key` == keccak(address) diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 51efbb60bb3ce..582205aaa63cf 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -381,7 +381,7 @@ async fn can_get_blobs_by_versioned_hash() { let hash = sidecar.versioned_hash_for_blob(0).unwrap(); // api.anvil_set_auto_mine(true).await.unwrap(); - let blob = api.anvil_get_blob_by_versioned_hash(hash).await.unwrap().unwrap(); + let blob = api.anvil_get_blob_by_versioned_hash(hash).unwrap().unwrap(); assert_eq!(blob.sidecar, sidecar); } @@ -418,6 +418,6 @@ async fn can_get_blobs_by_tx_hash() { let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let hash = receipt.transaction_hash; api.anvil_set_auto_mine(true).await.unwrap(); - let blob = api.anvil_get_blob_by_tx_hash(hash).await.unwrap().unwrap(); + let blob = api.anvil_get_blob_by_tx_hash(hash).unwrap().unwrap(); assert_eq!(blob.sidecar, sidecar); } From 06d49765382e99bb15a30de6c29e1e82bf3940ac Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Sat, 12 Jul 2025 15:50:32 +0530 Subject: [PATCH 6/9] refactoring and moving to backend --- crates/anvil/src/eth/backend/mem/mod.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 66bb6ea86f70c..9cbbfce46dd29 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2919,23 +2919,17 @@ impl Backend { } pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result> { - let storage = self.blockchain.storage.read(); - for (_, block) in &storage.blocks { - for tx in &block.transactions { - let typed_tx = tx.as_ref(); - if *typed_tx.hash() == hash { - if let Some(sidecar) = typed_tx.sidecar() { - return Ok(Some(sidecar.clone())); - } - } - } + let tx = self.mined_transaction_by_hash(hash).unwrap(); + let typed_tx = TypedTransaction::try_from(tx).unwrap(); + if let Some(sidecar) = typed_tx.sidecar() { + return Ok(Some(sidecar.clone())); } Ok(None) } pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result> { let storage = self.blockchain.storage.read(); - for (_, block) in &storage.blocks { + for block in storage.blocks.values() { for tx in &block.transactions { let typed_tx = tx.as_ref(); if let Some(sidecar) = typed_tx.sidecar() { From dd1a12c599864a822400f8aeb04f5d4701bb0cec Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Sat, 12 Jul 2025 19:00:23 +0530 Subject: [PATCH 7/9] vec blobs --- crates/anvil/src/eth/api.rs | 11 ++++------- crates/anvil/src/eth/backend/mem/mod.rs | 13 ++++++++----- crates/anvil/tests/it/eip4844.rs | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index f836d1ca1bec3..58e55c3f45b9d 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -33,7 +33,7 @@ use crate::{ mem::transaction_build, }; use alloy_consensus::{ - Account, + Account, Blob, transaction::{Recovered, eip4844::TxEip4844Variant}, }; use alloy_dyn_abi::TypedData; @@ -1327,12 +1327,9 @@ impl EthApi { Ok(self.backend.get_blob_by_versioned_hash(hash)?) } - /// Handler for RPC call: `anvil_getBlobByTransactionHash` - pub fn anvil_get_blob_by_tx_hash( - &self, - hash: B256, - ) -> Result> { - node_info!("anvil_getBlobByTransactionHash"); + /// Handler for RPC call: `anvil_getBlobsByTransactionHash` + pub fn anvil_get_blob_by_tx_hash(&self, hash: B256) -> Result>> { + node_info!("anvil_getBlobsByTransactionHash"); Ok(self.backend.get_blob_by_tx_hash(hash)?) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 9cbbfce46dd29..c3c72edc4812f 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2918,11 +2918,14 @@ impl Backend { )) } - pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result> { - let tx = self.mined_transaction_by_hash(hash).unwrap(); - let typed_tx = TypedTransaction::try_from(tx).unwrap(); - if let Some(sidecar) = typed_tx.sidecar() { - return Ok(Some(sidecar.clone())); + pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result>> { + // Try to get the mined transaction by hash + if let Some(tx) = self.mined_transaction_by_hash(hash) { + if let Ok(typed_tx) = TypedTransaction::try_from(tx) { + if let Some(sidecar) = typed_tx.sidecar() { + return Ok(Some(sidecar.sidecar.blobs.clone())); + } + } } Ok(None) } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 582205aaa63cf..332e4cb6aef66 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -418,6 +418,6 @@ async fn can_get_blobs_by_tx_hash() { let receipt = provider.send_transaction(tx).await.unwrap().get_receipt().await.unwrap(); let hash = receipt.transaction_hash; api.anvil_set_auto_mine(true).await.unwrap(); - let blob = api.anvil_get_blob_by_tx_hash(hash).unwrap().unwrap(); - assert_eq!(blob.sidecar, sidecar); + let blobs = api.anvil_get_blob_by_tx_hash(hash).unwrap().unwrap(); + assert_eq!(blobs, sidecar.blobs); } From e088a4c54bfa9d19e99df21a8ee150c6cdb56128 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Mon, 14 Jul 2025 17:58:59 +0530 Subject: [PATCH 8/9] get blob --- crates/anvil/src/eth/api.rs | 2 +- crates/anvil/src/eth/backend/mem/mod.rs | 21 ++++++++++++++------- crates/anvil/tests/it/eip4844.rs | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/crates/anvil/src/eth/api.rs b/crates/anvil/src/eth/api.rs index 58e55c3f45b9d..344bec84ce8c2 100644 --- a/crates/anvil/src/eth/api.rs +++ b/crates/anvil/src/eth/api.rs @@ -1322,7 +1322,7 @@ impl EthApi { pub fn anvil_get_blob_by_versioned_hash( &self, hash: B256, - ) -> Result> { + ) -> Result> { node_info!("anvil_getBlobByHash"); Ok(self.backend.get_blob_by_versioned_hash(hash)?) } diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index c3c72edc4812f..23930101145a2 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -34,12 +34,12 @@ use crate::{ }; use alloy_chains::NamedChain; use alloy_consensus::{ - Account, BlockHeader, EnvKzgSettings, Header, Receipt, ReceiptWithBloom, Signed, - Transaction as TransactionTrait, TxEip4844WithSidecar, TxEnvelope, + Account, Blob, BlockHeader, EnvKzgSettings, Header, Receipt, ReceiptWithBloom, Signed, + Transaction as TransactionTrait, TxEnvelope, proofs::{calculate_receipt_root, calculate_transaction_root}, transaction::Recovered, }; -use alloy_eips::{eip1559::BaseFeeParams, eip7840::BlobParams}; +use alloy_eips::{eip1559::BaseFeeParams, eip4844::kzg_to_versioned_hash, eip7840::BlobParams}; use alloy_evm::{Database, Evm, eth::EthEvmContext, precompiles::PrecompilesMap}; use alloy_network::{ AnyHeader, AnyRpcBlock, AnyRpcHeader, AnyRpcTransaction, AnyTxEnvelope, AnyTxType, @@ -2930,16 +2930,23 @@ impl Backend { Ok(None) } - pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result> { + pub fn get_blob_by_versioned_hash(&self, hash: B256) -> Result> { let storage = self.blockchain.storage.read(); for block in storage.blocks.values() { for tx in &block.transactions { let typed_tx = tx.as_ref(); if let Some(sidecar) = typed_tx.sidecar() { - for blob in sidecar.sidecar.clone() { - let versioned_hash = B256::from(blob.to_kzg_versioned_hash()); + for versioned_hash in sidecar.sidecar.versioned_hashes() { if versioned_hash == hash { - return Ok(Some(sidecar.clone())); + if let Some(index) = + sidecar.sidecar.commitments.iter().position(|commitment| { + kzg_to_versioned_hash(commitment.as_slice()) == *hash + }) + { + if let Some(blob) = sidecar.sidecar.blobs.get(index) { + return Ok(Some(blob.clone())); + } + } } } } diff --git a/crates/anvil/tests/it/eip4844.rs b/crates/anvil/tests/it/eip4844.rs index 332e4cb6aef66..292194aae61da 100644 --- a/crates/anvil/tests/it/eip4844.rs +++ b/crates/anvil/tests/it/eip4844.rs @@ -382,7 +382,7 @@ async fn can_get_blobs_by_versioned_hash() { let hash = sidecar.versioned_hash_for_blob(0).unwrap(); // api.anvil_set_auto_mine(true).await.unwrap(); let blob = api.anvil_get_blob_by_versioned_hash(hash).unwrap().unwrap(); - assert_eq!(blob.sidecar, sidecar); + assert_eq!(blob, sidecar.blobs[0]); } #[tokio::test(flavor = "multi_thread")] From f972f35e8b8a05d78a7c1b18f9d089915811dea6 Mon Sep 17 00:00:00 2001 From: Soubhik-10 Date: Mon, 14 Jul 2025 18:16:02 +0530 Subject: [PATCH 9/9] clippy --- crates/anvil/src/eth/backend/mem/mod.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/anvil/src/eth/backend/mem/mod.rs b/crates/anvil/src/eth/backend/mem/mod.rs index 23930101145a2..362f1d987dd48 100644 --- a/crates/anvil/src/eth/backend/mem/mod.rs +++ b/crates/anvil/src/eth/backend/mem/mod.rs @@ -2920,13 +2920,13 @@ impl Backend { pub fn get_blob_by_tx_hash(&self, hash: B256) -> Result>> { // Try to get the mined transaction by hash - if let Some(tx) = self.mined_transaction_by_hash(hash) { - if let Ok(typed_tx) = TypedTransaction::try_from(tx) { - if let Some(sidecar) = typed_tx.sidecar() { - return Ok(Some(sidecar.sidecar.blobs.clone())); - } - } + if let Some(tx) = self.mined_transaction_by_hash(hash) + && let Ok(typed_tx) = TypedTransaction::try_from(tx) + && let Some(sidecar) = typed_tx.sidecar() + { + return Ok(Some(sidecar.sidecar.blobs.clone())); } + Ok(None) } @@ -2937,16 +2937,14 @@ impl Backend { let typed_tx = tx.as_ref(); if let Some(sidecar) = typed_tx.sidecar() { for versioned_hash in sidecar.sidecar.versioned_hashes() { - if versioned_hash == hash { - if let Some(index) = + if versioned_hash == hash + && let Some(index) = sidecar.sidecar.commitments.iter().position(|commitment| { kzg_to_versioned_hash(commitment.as_slice()) == *hash }) - { - if let Some(blob) = sidecar.sidecar.blobs.get(index) { - return Ok(Some(blob.clone())); - } - } + && let Some(blob) = sidecar.sidecar.blobs.get(index) + { + return Ok(Some(*blob)); } } }