From 77dbd2809f29c638cd3068f09c75e9c3b8eaa604 Mon Sep 17 00:00:00 2001 From: Eshaan Bansal Date: Sat, 22 Feb 2025 13:27:37 +0530 Subject: [PATCH] add get_block and make code optional --- common/src/types.rs | 2 +- core/src/client/node.rs | 14 +++++--- core/src/execution/client/mod.rs | 2 ++ core/src/execution/client/rpc.rs | 36 +++++++++++++++------ core/src/execution/client/verifiable_api.rs | 34 +++++++++++-------- core/src/execution/evm.rs | 8 ++--- core/src/execution/mod.rs | 7 ++-- verifiable-api/client/src/lib.rs | 13 +++++++- verifiable-api/server/bin/handlers.rs | 16 ++++++++- verifiable-api/server/bin/router.rs | 3 +- verifiable-api/server/bin/server.rs | 4 +-- verifiable-api/server/bin/service.rs | 26 ++++++++++++--- verifiable-api/types/src/lib.rs | 3 +- 13 files changed, 122 insertions(+), 46 deletions(-) diff --git a/common/src/types.rs b/common/src/types.rs index 93a349a2..210c9665 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -13,7 +13,7 @@ pub struct Account { pub balance: U256, pub nonce: u64, pub code_hash: B256, - pub code: Vec, + pub code: Option>, pub storage_hash: B256, pub slots: HashMap, } diff --git a/core/src/client/node.rs b/core/src/client/node.rs index 0ce33355..5a04a3b5 100644 --- a/core/src/client/node.rs +++ b/core/src/client/node.rs @@ -80,14 +80,20 @@ impl> Node { pub async fn get_balance(&self, address: Address, tag: BlockTag) -> Result { self.check_blocktag_age(&tag).await?; - let account = self.execution.get_account(address, None, tag).await?; + let account = self + .execution + .get_account(address, None, tag, false) + .await?; Ok(account.balance) } pub async fn get_nonce(&self, address: Address, tag: BlockTag) -> Result { self.check_blocktag_age(&tag).await?; - let account = self.execution.get_account(address, None, tag).await?; + let account = self + .execution + .get_account(address, None, tag, false) + .await?; Ok(account.nonce) } @@ -107,8 +113,8 @@ impl> Node { pub async fn get_code(&self, address: Address, tag: BlockTag) -> Result { self.check_blocktag_age(&tag).await?; - let account = self.execution.get_account(address, None, tag).await?; - Ok(account.code.into()) + let account = self.execution.get_account(address, None, tag, true).await?; + Ok(account.code.unwrap().into()) } pub async fn get_storage_at( diff --git a/core/src/execution/client/mod.rs b/core/src/execution/client/mod.rs index b75ae163..69641dd4 100644 --- a/core/src/execution/client/mod.rs +++ b/core/src/execution/client/mod.rs @@ -29,6 +29,7 @@ pub trait ExecutionMethods>: Send + Sync { address: Address, slots: Option<&[B256]>, tag: BlockTag, + include_code: bool, ) -> Result; async fn get_transaction_receipt(&self, tx_hash: B256) -> Result>; async fn get_logs(&self, filter: &Filter) -> Result>; @@ -40,6 +41,7 @@ pub trait ExecutionMethods>: Send + Sync { block: Option, ) -> Result>; async fn chain_id(&self) -> Result; + async fn get_block(&self, block_id: BlockId) -> Result>; async fn get_block_receipts(&self, block: BlockId) -> Result>>; async fn send_raw_transaction(&self, bytes: &[u8]) -> Result; async fn new_filter(&self, filter: &Filter) -> Result; diff --git a/core/src/execution/client/rpc.rs b/core/src/execution/client/rpc.rs index 9ba75a4a..4867d238 100644 --- a/core/src/execution/client/rpc.rs +++ b/core/src/execution/client/rpc.rs @@ -9,7 +9,7 @@ use alloy::rpc::types::{EIP1186AccountProofResponse, Filter, FilterChanges, Log} use async_trait::async_trait; use eyre::Result; use futures::future::{join_all, try_join_all}; -use revm::primitives::{AccessListItem, KECCAK_EMPTY}; +use revm::primitives::AccessListItem; use helios_common::{ network_spec::NetworkSpec, @@ -47,6 +47,7 @@ impl> ExecutionMethods for ExecutionRpc address: Address, slots: Option<&[B256]>, tag: BlockTag, + include_code: bool, ) -> Result { let slots = slots.unwrap_or(&[]); let block = self @@ -60,7 +61,8 @@ impl> ExecutionMethods for ExecutionRpc .get_proof(address, slots, block.header().number().into()) .await?; - self.verify_proof_to_account(&proof, &block).await + self.verify_proof_to_account(&proof, &block, include_code) + .await } async fn get_transaction_receipt(&self, tx_hash: B256) -> Result> { @@ -178,8 +180,12 @@ impl> ExecutionMethods for ExecutionRpc let mut account_map = HashMap::new(); for chunk in list.chunks(PARALLEL_QUERY_BATCH_SIZE) { let account_chunk_futs = chunk.iter().map(|account| { - let account_fut = - self.get_account(account.address, Some(account.storage_keys.as_slice()), tag); + let account_fut = self.get_account( + account.address, + Some(account.storage_keys.as_slice()), + tag, + true, + ); async move { (account.address, account_fut.await) } }); @@ -198,6 +204,17 @@ impl> ExecutionMethods for ExecutionRpc self.rpc.chain_id().await } + async fn get_block(&self, block: BlockId) -> Result> { + Ok(match block { + BlockId::Number(number_or_tag) => { + self.rpc + .get_block_by_number(number_or_tag, false.into()) + .await? + } + BlockId::Hash(hash) => self.rpc.get_block(hash.into()).await.ok(), + }) + } + async fn get_block_receipts(&self, block: BlockId) -> Result>> { self.rpc.get_block_receipts(block).await } @@ -228,21 +245,19 @@ impl> ExecutionRpcClient { &self, proof: &EIP1186AccountProofResponse, block: &N::BlockResponse, + verify_code: bool, ) -> Result { // Verify the account proof verify_account_proof(proof, block.header().state_root())?; // Verify the storage proofs, collecting the slot values let slot_map = verify_storage_proof(proof)?; // Verify the code hash - let code = if proof.code_hash == KECCAK_EMPTY || proof.code_hash == B256::ZERO { - Vec::new() - } else { + let code = if verify_code { let code = self .rpc .get_code(proof.address, block.header().number().into()) .await?; let code_hash = keccak256(&code); - if proof.code_hash != code_hash { return Err(ExecutionError::CodeHashMismatch( proof.address, @@ -251,8 +266,9 @@ impl> ExecutionRpcClient { ) .into()); } - - code + Some(code) + } else { + None }; Ok(Account { diff --git a/core/src/execution/client/verifiable_api.rs b/core/src/execution/client/verifiable_api.rs index da79a542..80dcd40e 100644 --- a/core/src/execution/client/verifiable_api.rs +++ b/core/src/execution/client/verifiable_api.rs @@ -6,7 +6,6 @@ use alloy::network::{BlockResponse, ReceiptResponse}; use alloy::primitives::{keccak256, Address, B256, U256}; use alloy::rlp; use alloy::rpc::types::{EIP1186AccountProofResponse, Filter, FilterChanges, Log}; -use alloy_trie::KECCAK_EMPTY; use async_trait::async_trait; use eyre::Result; @@ -43,6 +42,7 @@ impl, A: VerifiableApi> ExecutionMethods, tag: BlockTag, + include_code: bool, ) -> Result { let block = self .state @@ -58,7 +58,7 @@ impl, A: VerifiableApi> ExecutionMethods, A: VerifiableApi> ExecutionMethods Result> { + self.api.get_block(block).await + } + async fn get_block_receipts(&self, block: BlockId) -> Result>> { self.api.get_block_receipts(block).await } @@ -199,19 +203,21 @@ impl, A: VerifiableApi> verify_account_proof(&proof, block.header().state_root())?; // Verify the storage proofs, collecting the slot values let slot_map = verify_storage_proof(&proof)?; - // Verify the code hash - let code = if proof.code_hash == KECCAK_EMPTY || proof.code_hash == B256::ZERO { - Vec::new() - } else { - let code_hash = keccak256(&account.code); - - if proof.code_hash != code_hash { - return Err( - ExecutionError::CodeHashMismatch(address, code_hash, proof.code_hash).into(), - ); + // Verify the code hash (if code is included in the response) + let code = match account.code { + Some(code) => { + let code_hash = keccak256(&code); + if proof.code_hash != code_hash { + return Err(ExecutionError::CodeHashMismatch( + address, + code_hash, + proof.code_hash, + ) + .into()); + } + Some(code.into()) } - - account.code.into() + None => None, }; Ok(Account { diff --git a/core/src/execution/evm.rs b/core/src/execution/evm.rs index d20ad70b..039fb8c9 100644 --- a/core/src/execution/evm.rs +++ b/core/src/execution/evm.rs @@ -159,7 +159,7 @@ impl, A: VerifiableApi> EvmState StateAccess::Basic(address) => { let account = self .execution - .get_account(*address, None, self.block) + .get_account(*address, None, self.block, true) .await?; self.basic.insert( @@ -168,7 +168,7 @@ impl, A: VerifiableApi> EvmState account.balance, account.nonce, account.code_hash, - Bytecode::new_raw(account.code.into()), + Bytecode::new_raw(account.code.unwrap().into()), ), ); } @@ -176,7 +176,7 @@ impl, A: VerifiableApi> EvmState let slot_bytes = B256::from(*slot); let account = self .execution - .get_account(*address, Some(&[slot_bytes]), self.block) + .get_account(*address, Some(&[slot_bytes]), self.block, false) .await?; let storage = self.storage.entry(*address).or_default(); @@ -246,7 +246,7 @@ impl, A: VerifiableApi> EvmState account.balance, account.nonce, account.code_hash, - Bytecode::new_raw(account.code.into()), + Bytecode::new_raw(account.code.unwrap().into()), ), ); diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index b83c0818..53f2eaa4 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -85,8 +85,11 @@ impl, A: VerifiableApi> ExecutionClient, tag: BlockTag, + include_code: bool, ) -> Result { - self.client().get_account(address, slots, tag).await + self.client() + .get_account(address, slots, tag, include_code) + .await } pub async fn get_storage_at( @@ -98,7 +101,7 @@ impl, A: VerifiableApi> ExecutionClient: Send + Clone + Sync + 'static { address: Address, storage_slots: &[U256], block: Option, + include_code: bool, ) -> Result; async fn get_transaction_receipt( &self, @@ -40,6 +41,7 @@ pub trait VerifiableApi: Send + Clone + Sync + 'static { ) -> Result; // Methods just for compatibility (acts as a proxy) async fn chain_id(&self) -> Result; + async fn get_block(&self, block_id: BlockId) -> Result>; async fn get_block_receipts(&self, block: BlockId) -> Result>>; async fn send_raw_transaction(&self, bytes: &[u8]) -> Result; async fn new_filter(&self, filter: &Filter) -> Result; @@ -77,6 +79,7 @@ impl VerifiableApi for VerifiableApiClient { address: Address, storage_slots: &[U256], block: Option, + include_code: bool, ) -> Result { let url = format!("{}/eth/v1/proof/account/{}", self.base_url, address); let mut request = self.client.get(&url); @@ -86,6 +89,7 @@ impl VerifiableApi for VerifiableApiClient { for slot in storage_slots { request = request.query(&[("storageSlots", slot)]); } + request = request.query(&[("includeCode", include_code)]); let response = request.send().await?; let response = response.json::().await?; Ok(response) @@ -188,8 +192,15 @@ impl VerifiableApi for VerifiableApiClient { Ok(response) } + async fn get_block(&self, block_id: BlockId) -> Result> { + let url = format!("{}/eth/v1/block/{}", self.base_url, block_id); + let response = self.client.get(&url).send().await?; + let response = response.json::>().await?; + Ok(response) + } + async fn get_block_receipts(&self, block: BlockId) -> Result>> { - let url = format!("{}/eth/v1/block_receipts/{}", self.base_url, block); + let url = format!("{}/eth/v1/block/{}/receipts", self.base_url, block); let response = self.client.get(&url).send().await?; let response = response.json::>>().await?; Ok(response) diff --git a/verifiable-api/server/bin/handlers.rs b/verifiable-api/server/bin/handlers.rs index 7d1fbaf9..5cef6564 100644 --- a/verifiable-api/server/bin/handlers.rs +++ b/verifiable-api/server/bin/handlers.rs @@ -39,6 +39,8 @@ fn map_server_err(e: Report) -> (StatusCode, Json) { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] pub struct AccountProofQuery { + #[serde(default)] + include_code: bool, #[serde(default)] pub storage_slots: Vec, pub block: Option, @@ -105,13 +107,14 @@ impl TryInto for LogsQuery { pub async fn get_account>( Path(address): Path
, Query(AccountProofQuery { + include_code, storage_slots, block, }): Query, State(ApiState { api_service }): State>, ) -> Response { api_service - .get_account(address, &storage_slots, block) + .get_account(address, &storage_slots, block, include_code) .await .map(Json) .map_err(map_server_err) @@ -184,6 +187,17 @@ pub async fn get_chain_id>( .map_err(map_server_err) } +pub async fn get_block>( + Path(block_id): Path, + State(ApiState { api_service }): State>, +) -> Response> { + api_service + .get_block(block_id) + .await + .map(Json) + .map_err(map_server_err) +} + pub async fn get_block_receipts>( Path(block_id): Path, State(ApiState { api_service }): State>, diff --git a/verifiable-api/server/bin/router.rs b/verifiable-api/server/bin/router.rs index 937ee251..21ecb22d 100644 --- a/verifiable-api/server/bin/router.rs +++ b/verifiable-api/server/bin/router.rs @@ -31,8 +31,9 @@ pub fn build_router>() -> Router>::new(&execution_rpc.as_str()); + ApiService::>::new(execution_rpc.as_str()); let router = build_router().with_state(ApiState { api_service }); (server_addr, router) } @@ -41,7 +41,7 @@ async fn main() { let server_addr = args.server_address; let execution_rpc = args.execution_rpc; let api_service = - ApiService::>::new(&execution_rpc.as_str()); + ApiService::>::new(execution_rpc.as_str()); let router = build_router().with_state(ApiState { api_service }); (server_addr, router) } diff --git a/verifiable-api/server/bin/service.rs b/verifiable-api/server/bin/service.rs index 0e7de94f..7edbfe9c 100644 --- a/verifiable-api/server/bin/service.rs +++ b/verifiable-api/server/bin/service.rs @@ -35,7 +35,7 @@ impl> VerifiableApi for ApiService { fn new(rpc: &str) -> Self { Self { rpc: Arc::new(ExecutionRpc::new(rpc).unwrap()), - _marker: PhantomData::default(), + _marker: Default::default(), } } @@ -44,6 +44,7 @@ impl> VerifiableApi for ApiService { address: Address, storage_slots: &[U256], block: Option, + include_code: bool, ) -> Result { let block = block.unwrap_or_default(); // make sure block ID is not tag but a number or hash @@ -63,12 +64,16 @@ impl> VerifiableApi for ApiService { }?; let storage_keys = storage_slots - .into_iter() + .iter() .map(|key| (*key).into()) .collect::>(); let proof = self.rpc.get_proof(address, &storage_keys, block).await?; - let code = self.rpc.get_code(address, block).await?; + let code = if include_code { + Some(self.rpc.get_code(address, block).await?.into()) + } else { + None + }; Ok(AccountResponse { account: Account { @@ -77,7 +82,7 @@ impl> VerifiableApi for ApiService { code_hash: proof.code_hash, storage_root: proof.storage_hash, }, - code: code.into(), + code, account_proof: proof.account_proof, storage_proof: proof.storage_proof, }) @@ -199,7 +204,7 @@ impl> VerifiableApi for ApiService { .iter() .map(|key| (*key).into()) .collect::>(); - let account_fut = self.get_account(account.address, &slots, Some(block_id)); + let account_fut = self.get_account(account.address, &slots, Some(block_id), true); (account.address, account_fut.await) }); @@ -220,6 +225,17 @@ impl> VerifiableApi for ApiService { }) } + async fn get_block(&self, block: BlockId) -> Result> { + Ok(match block { + BlockId::Number(number_or_tag) => { + self.rpc + .get_block_by_number(number_or_tag, false.into()) + .await? + } + BlockId::Hash(hash) => self.rpc.get_block(hash.into()).await.ok(), + }) + } + async fn get_block_receipts(&self, block: BlockId) -> Result>> { self.rpc.get_block_receipts(block).await } diff --git a/verifiable-api/types/src/lib.rs b/verifiable-api/types/src/lib.rs index 8633797f..87a5cb1f 100644 --- a/verifiable-api/types/src/lib.rs +++ b/verifiable-api/types/src/lib.rs @@ -14,7 +14,8 @@ use helios_common::network_spec::NetworkSpec; #[serde(rename_all = "camelCase")] pub struct AccountResponse { pub account: TrieAccount, - pub code: Bytes, + #[serde(skip_serializing_if = "Option::is_none")] + pub code: Option, pub account_proof: Vec, pub storage_proof: Vec, }