diff --git a/Cargo.lock b/Cargo.lock index e55cf2fc..401e0b8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3115,6 +3115,7 @@ name = "helios-common" version = "0.1.0" dependencies = [ "alloy", + "eyre", "revm", "serde", "serde_json", diff --git a/common/Cargo.toml b/common/Cargo.toml index 7687174d..5ad627a6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -8,3 +8,4 @@ alloy.workspace = true serde.workspace = true serde_json.workspace = true revm.workspace = true +eyre.workspace = true diff --git a/common/src/types.rs b/common/src/types.rs index 019f0b24..56587a5b 100644 --- a/common/src/types.rs +++ b/common/src/types.rs @@ -1,7 +1,11 @@ use std::collections::HashMap; use std::fmt::Display; -use alloy::primitives::{B256, U256}; +use alloy::{ + eips::{BlockId, BlockNumberOrTag}, + primitives::{B256, U256}, +}; +use eyre::{eyre, Report, Result}; use serde::{de::Error, Deserialize, Serialize}; #[derive(Default, Debug, Clone, Serialize, Deserialize)] @@ -61,3 +65,27 @@ impl<'de> Deserialize<'de> for BlockTag { Ok(block_tag) } } + +impl Into for BlockTag { + fn into(self) -> BlockId { + match self { + BlockTag::Latest => BlockId::latest(), + BlockTag::Finalized => BlockId::finalized(), + BlockTag::Number(num) => BlockId::Number(num.into()), + } + } +} + +impl TryFrom for BlockTag { + type Error = Report; + + fn try_from(block_id: BlockId) -> Result { + match block_id { + BlockId::Number(BlockNumberOrTag::Number(num)) => Ok(BlockTag::Number(num)), + BlockId::Number(BlockNumberOrTag::Latest) => Ok(BlockTag::Latest), + BlockId::Number(BlockNumberOrTag::Finalized) => Ok(BlockTag::Finalized), + BlockId::Number(other) => Err(eyre!("BlockId::Number({other}) is not supported")), + BlockId::Hash(_) => Err(eyre!("BlockId::Hash is not supported")), + } + } +} diff --git a/core/src/execution/evm.rs b/core/src/execution/evm.rs index 15bf77a2..94ea1743 100644 --- a/core/src/execution/evm.rs +++ b/core/src/execution/evm.rs @@ -1,25 +1,20 @@ use std::{borrow::BorrowMut, collections::HashMap, sync::Arc}; -use alloy::{ - consensus::BlockHeader, - network::{primitives::HeaderResponse, BlockResponse, TransactionBuilder}, -}; +use alloy::network::{primitives::HeaderResponse, BlockResponse}; use eyre::{Report, Result}; -use futures::future::join_all; -use helios_verifiable_api_client::VerifiableApi; use revm::{ primitives::{ - address, AccessListItem, AccountInfo, Address, Bytecode, Bytes, Env, ExecutionResult, - ResultAndState, B256, U256, + address, AccountInfo, Address, Bytecode, Bytes, Env, ExecutionResult, ResultAndState, B256, + U256, }, Database, Evm as Revm, }; use tracing::trace; use helios_common::{fork_schedule::ForkSchedule, network_spec::NetworkSpec, types::BlockTag}; +use helios_verifiable_api_client::VerifiableApi; use crate::execution::{ - constants::PARALLEL_QUERY_BATCH_SIZE, errors::{EvmError, ExecutionError}, rpc::ExecutionRpc, ExecutionClient, @@ -237,70 +232,12 @@ impl, A: VerifiableApi> EvmState } pub async fn prefetch_state(&mut self, tx: &N::TransactionRequest) -> Result<()> { - let mut list = self - .execution - .rpc - .create_access_list(tx, self.block) - .await - .map_err(EvmError::RpcError)? - .0; - - let from_access_entry = AccessListItem { - address: tx.from().unwrap_or_default(), - storage_keys: Vec::default(), - }; - - let to_access_entry = AccessListItem { - address: tx.to().unwrap_or_default(), - storage_keys: Vec::default(), - }; - - let coinbase = self + let block_id = Some(self.block.into()); + let account_map = self .execution - .get_block(self.block, false) + .create_access_list(tx, block_id) .await - .ok_or(ExecutionError::BlockNotFound(self.block))? - .header() - .beneficiary(); - let producer_access_entry = AccessListItem { - address: coinbase, - storage_keys: Vec::default(), - }; - - let list_addresses = list.iter().map(|elem| elem.address).collect::>(); - - if !list_addresses.contains(&from_access_entry.address) { - list.push(from_access_entry) - } - - if !list_addresses.contains(&to_access_entry.address) { - list.push(to_access_entry) - } - - if !list_addresses.contains(&producer_access_entry.address) { - list.push(producer_access_entry) - } - - 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.execution.get_account( - account.address, - Some(account.storage_keys.as_slice()), - self.block, - ); - async move { (account.address, account_fut.await) } - }); - - let account_chunk = join_all(account_chunk_futs).await; - - account_chunk - .into_iter() - .filter(|i| i.1.is_ok()) - .for_each(|(key, value)| { - account_map.insert(key, value.ok().unwrap()); - }); - } + .map_err(EvmError::RpcError)?; for (address, account) in account_map { self.basic.insert( diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index 104b421a..0f3f317c 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::marker::PhantomData; use alloy::consensus::BlockHeader; @@ -339,4 +340,15 @@ impl, A: VerifiableApi> ExecutionClient, + ) -> Result> { + self.verified_methods + .client() + .create_access_list(tx, block) + .await + } } diff --git a/core/src/execution/rpc/http_rpc.rs b/core/src/execution/rpc/http_rpc.rs index 743ae85d..0fc21dcb 100644 --- a/core/src/execution/rpc/http_rpc.rs +++ b/core/src/execution/rpc/http_rpc.rs @@ -13,7 +13,7 @@ use eyre::{eyre, Result}; use reqwest::Client; use revm::primitives::AccessList; -use helios_common::{network_spec::NetworkSpec, types::BlockTag}; +use helios_common::network_spec::NetworkSpec; use crate::errors::RpcError; @@ -72,14 +72,8 @@ impl ExecutionRpc for HttpRpc { async fn create_access_list( &self, tx: &N::TransactionRequest, - block: BlockTag, + block: BlockId, ) -> Result { - let block = match block { - BlockTag::Latest => BlockId::latest(), - BlockTag::Finalized => BlockId::finalized(), - BlockTag::Number(num) => BlockId::number(num), - }; - let list = self .provider .create_access_list(tx) diff --git a/core/src/execution/rpc/mock_rpc.rs b/core/src/execution/rpc/mock_rpc.rs index 6d67231b..39a20cbf 100644 --- a/core/src/execution/rpc/mock_rpc.rs +++ b/core/src/execution/rpc/mock_rpc.rs @@ -9,7 +9,7 @@ use alloy::rpc::types::{ use async_trait::async_trait; use eyre::{eyre, Result}; -use helios_common::{network_spec::NetworkSpec, types::BlockTag}; +use helios_common::network_spec::NetworkSpec; use super::ExecutionRpc; @@ -39,7 +39,7 @@ impl ExecutionRpc for MockRpc { async fn create_access_list( &self, _opts: &N::TransactionRequest, - _block: BlockTag, + _block: BlockId, ) -> Result { Err(eyre!("not implemented")) } diff --git a/core/src/execution/rpc/mod.rs b/core/src/execution/rpc/mod.rs index 4e781ff4..8752cda7 100644 --- a/core/src/execution/rpc/mod.rs +++ b/core/src/execution/rpc/mod.rs @@ -7,7 +7,7 @@ use alloy::rpc::types::{ use async_trait::async_trait; use eyre::Result; -use helios_common::{network_spec::NetworkSpec, types::BlockTag}; +use helios_common::network_spec::NetworkSpec; pub mod http_rpc; pub mod mock_rpc; @@ -29,7 +29,7 @@ pub trait ExecutionRpc: Send + Clone + Sync + 'static { async fn create_access_list( &self, tx: &N::TransactionRequest, - block: BlockTag, + block: BlockId, ) -> Result; async fn get_code(&self, address: Address, block: u64) -> Result>; diff --git a/core/src/execution/verified_client/api.rs b/core/src/execution/verified_client/api.rs index 0016adcb..0cb86edc 100644 --- a/core/src/execution/verified_client/api.rs +++ b/core/src/execution/verified_client/api.rs @@ -51,49 +51,9 @@ impl, A: VerifiableApi> VerifiableMethods< .ok_or(ExecutionError::BlockNotFound(tag))?; let block_id = BlockId::number(block.header().number()); - let AccountResponse { - account, - code, - account_proof, - storage_proof, - } = self.api.get_account(address, slots, Some(block_id)).await?; + let account_response = self.api.get_account(address, slots, Some(block_id)).await?; - // Verify the account proof - let proof = EIP1186AccountProofResponse { - address, - balance: account.balance, - code_hash: account.code_hash, - nonce: account.nonce, - storage_hash: account.storage_root, - account_proof, - storage_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_hash = keccak256(&code); - - if proof.code_hash != code_hash { - return Err( - ExecutionError::CodeHashMismatch(address, code_hash, proof.code_hash).into(), - ); - } - - code.into() - }; - - Ok(Account { - balance: account.balance, - nonce: account.nonce, - code_hash: account.code_hash, - code, - storage_hash: account.storage_root, - slots: slot_map, - }) + self.verify_account_response(address, account_response, &block) } async fn get_transaction_receipt(&self, tx_hash: B256) -> Result> { @@ -161,9 +121,83 @@ impl, A: VerifiableApi> VerifiableMethods< Ok(logs) } + + async fn create_access_list( + &self, + tx: &N::TransactionRequest, + block: Option, + ) -> Result> { + let block_id = block.unwrap_or(BlockId::latest()); + let tag = BlockTag::try_from(block_id)?; + let block = self + .state + .get_block(tag) + .await + .ok_or(ExecutionError::BlockNotFound(tag))?; + let block_id = BlockId::Number(block.header().number().into()); + + let AccessListResponse { accounts } = self + .api + .create_access_list(tx.clone(), Some(block_id)) + .await?; + + let account_map = accounts + .into_iter() + .map(|(address, account_response)| { + self.verify_account_response(address, account_response, &block) + .map(|account| (address, account)) + }) + .collect::>>()?; + + Ok(account_map) + } } impl, A: VerifiableApi> VerifiableMethodsApi { + fn verify_account_response( + &self, + address: Address, + account: AccountResponse, + block: &N::BlockResponse, + ) -> Result { + let proof = EIP1186AccountProofResponse { + address, + balance: account.account.balance, + code_hash: account.account.code_hash, + nonce: account.account.nonce, + storage_hash: account.account.storage_root, + account_proof: account.account_proof, + storage_proof: account.storage_proof, + }; + // 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_hash = keccak256(&account.code); + + if proof.code_hash != code_hash { + return Err( + ExecutionError::CodeHashMismatch(address, code_hash, proof.code_hash).into(), + ); + } + + account.code.into() + }; + + Ok(Account { + balance: proof.balance, + nonce: proof.nonce, + code_hash: proof.code_hash, + code, + storage_hash: proof.storage_hash, + slots: slot_map, + }) + } + async fn verify_logs_and_receipts( &self, logs: &[Log], diff --git a/core/src/execution/verified_client/mod.rs b/core/src/execution/verified_client/mod.rs index 72c18477..a8a7d078 100644 --- a/core/src/execution/verified_client/mod.rs +++ b/core/src/execution/verified_client/mod.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; + +use alloy::eips::BlockId; use alloy::primitives::{Address, B256, U256}; use alloy::rpc::types::{Filter, FilterChanges, Log}; use async_trait::async_trait; @@ -30,6 +33,11 @@ pub trait VerifiableMethods> { async fn get_logs(&self, filter: &Filter) -> Result>; async fn get_filter_changes(&self, filter_id: U256) -> Result; async fn get_filter_logs(&self, filter_id: U256) -> Result>; + async fn create_access_list( + &self, + tx: &N::TransactionRequest, + block: Option, + ) -> Result>; } #[derive(Clone)] diff --git a/core/src/execution/verified_client/rpc.rs b/core/src/execution/verified_client/rpc.rs index d7ba8e21..8f9bac90 100644 --- a/core/src/execution/verified_client/rpc.rs +++ b/core/src/execution/verified_client/rpc.rs @@ -2,21 +2,21 @@ use std::collections::{HashMap, HashSet}; use alloy::consensus::BlockHeader; use alloy::eips::BlockId; -use alloy::network::{BlockResponse, ReceiptResponse}; +use alloy::network::{BlockResponse, ReceiptResponse, TransactionBuilder}; use alloy::primitives::{keccak256, Address, B256, U256}; use alloy::rlp; -use alloy::rpc::types::{Filter, FilterChanges, Log}; +use alloy::rpc::types::{EIP1186AccountProofResponse, Filter, FilterChanges, Log}; use async_trait::async_trait; use eyre::Result; -use futures::future::try_join_all; -use revm::primitives::KECCAK_EMPTY; +use futures::future::{join_all, try_join_all}; +use revm::primitives::{AccessListItem, KECCAK_EMPTY}; use helios_common::{ network_spec::NetworkSpec, types::{Account, BlockTag}, }; -use crate::execution::constants::MAX_SUPPORTED_LOGS_NUMBER; +use crate::execution::constants::{MAX_SUPPORTED_LOGS_NUMBER, PARALLEL_QUERY_BATCH_SIZE}; use crate::execution::errors::ExecutionError; use crate::execution::proof::{ ordered_trie_root_noop_encoder, verify_account_proof, verify_storage_proof, @@ -57,34 +57,7 @@ impl> VerifiableMethods for VerifiableM .get_proof(address, slots, block.header().number().into()) .await?; - // 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 = self.rpc.get_code(address, block.header().number()).await?; - let code_hash = keccak256(&code); - - if proof.code_hash != code_hash { - return Err( - ExecutionError::CodeHashMismatch(address, code_hash, proof.code_hash).into(), - ); - } - - code - }; - - Ok(Account { - balance: proof.balance, - nonce: proof.nonce, - code, - code_hash: proof.code_hash, - storage_hash: proof.storage_hash, - slots: slot_map, - }) + self.verify_proof_to_account(&proof, &block).await } async fn get_transaction_receipt(&self, tx_hash: B256) -> Result> { @@ -174,9 +147,112 @@ impl> VerifiableMethods for VerifiableM self.verify_logs(&logs).await?; Ok(logs) } + + async fn create_access_list( + &self, + tx: &N::TransactionRequest, + block: Option, + ) -> Result> { + let block_id = block.unwrap_or(BlockId::latest()); + let tag = BlockTag::try_from(block_id)?; + let block = self + .state + .get_block(tag) + .await + .ok_or(ExecutionError::BlockNotFound(tag))?; + let block_id = BlockId::Number(block.header().number().into()); + + let mut list = self.rpc.create_access_list(tx, block_id).await?.0; + + let from_access_entry = AccessListItem { + address: tx.from().unwrap_or_default(), + storage_keys: Vec::default(), + }; + let to_access_entry = AccessListItem { + address: tx.to().unwrap_or_default(), + storage_keys: Vec::default(), + }; + let producer_access_entry = AccessListItem { + address: block.header().beneficiary(), + storage_keys: Vec::default(), + }; + + let list_addresses = list.iter().map(|elem| elem.address).collect::>(); + + if !list_addresses.contains(&from_access_entry.address) { + list.push(from_access_entry) + } + if !list_addresses.contains(&to_access_entry.address) { + list.push(to_access_entry) + } + if !list_addresses.contains(&producer_access_entry.address) { + list.push(producer_access_entry) + } + + let mut account_map = HashMap::new(); + for chunk in list.chunks(PARALLEL_QUERY_BATCH_SIZE) { + let account_chunk_futs = chunk.iter().map(|account| { + // ToDo(@eshaan7): block is fetched from state each iteration, can be optimized + let account_fut = + self.get_account(account.address, Some(account.storage_keys.as_slice()), tag); + async move { (account.address, account_fut.await) } + }); + + let account_chunk = join_all(account_chunk_futs).await; + + account_chunk.into_iter().for_each(|(address, value)| { + if let Some(account) = value.ok() { + account_map.insert(address, account); + } + }); + } + + Ok(account_map) + } } impl> VerifiableMethodsRpc { + async fn verify_proof_to_account( + &self, + proof: &EIP1186AccountProofResponse, + block: &N::BlockResponse, + ) -> 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 = self + .rpc + .get_code(proof.address, block.header().number()) + .await?; + let code_hash = keccak256(&code); + + if proof.code_hash != code_hash { + return Err(ExecutionError::CodeHashMismatch( + proof.address, + code_hash, + proof.code_hash, + ) + .into()); + } + + code + }; + + Ok(Account { + balance: proof.balance, + nonce: proof.nonce, + code_hash: proof.code_hash, + code, + storage_hash: proof.storage_hash, + slots: slot_map, + }) + } + /// Verify the integrity of each log entry in the given array of logs by /// checking its inclusion in the corresponding transaction receipt /// and verifying the transaction receipt itself against the block's receipt root. diff --git a/verifiable-api/README.md b/verifiable-api/README.md index d5968757..6ed00c9f 100644 --- a/verifiable-api/README.md +++ b/verifiable-api/README.md @@ -5,7 +5,7 @@ | Ethereum JSON-RPC Method | Helios Verifiable API Endpoint | |--------------------------------|-------------------------------------------------------------------------------| | `eth_getProof` | `/eth/v1/proof/account/{address}?storageKeys={}&block={}` | -| `eth_getBalance` | `/eth/v1/proof/account/{address}?storageKeys={}&block={}` | +| `eth_getBalance` | `/eth/v1/proof/account/{address}?storageKeys={}&block={}` | | `eth_getTransactionCount` | `/eth/v1/proof/account/{address}?storageKeys={}&block={}` | | `eth_getCode` | `/eth/v1/proof/account/{address}?storageKeys={}&block={}` | | `eth_getStorageAt` | `/eth/v1/proof/storage/{address}/{slot}?block={}` | @@ -14,3 +14,4 @@ | `eth_getLogs` | `/eth/v1/proof/logs?fromBlock={}&toBlock={}&blockHash={}&address={}&topics={}`| | `eth_getFilterLogs` | `/eth/v1/proof/filter_logs/{filter_id}` | | `eth_getFilterChanges` | `/eth/v1/proof/filter_changes/{filter_id}` | +| `eth_createAccessList` | `/eth/v1/proof/create_access_list` | diff --git a/verifiable-api/client/src/lib.rs b/verifiable-api/client/src/lib.rs index f63a6a18..16005d52 100644 --- a/verifiable-api/client/src/lib.rs +++ b/verifiable-api/client/src/lib.rs @@ -38,6 +38,11 @@ pub trait VerifiableApi: Send + Clone + Sync { async fn get_logs(&self, filter: &Filter) -> Result>; async fn get_filter_logs(&self, filter_id: U256) -> Result>; async fn get_filter_changes(&self, filter_id: U256) -> Result>; + async fn create_access_list( + &self, + tx: N::TransactionRequest, + block: Option, + ) -> Result; } pub struct VerifiableApiClient { @@ -163,4 +168,20 @@ impl VerifiableApi for VerifiableApiClient { let response = response.json::>().await?; Ok(response) } + + async fn create_access_list( + &self, + tx: N::TransactionRequest, + block: Option, + ) -> Result { + let url = format!("{}/eth/v1/proof/create_access_list", self.base_url); + let response = self + .client + .post(&url) + .json(&AccessListRequest:: { tx, block }) + .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 da1ea7d2..1597a626 100644 --- a/verifiable-api/server/bin/handlers.rs +++ b/verifiable-api/server/bin/handlers.rs @@ -6,9 +6,9 @@ use std::{ use alloy::{ consensus::{Account, BlockHeader}, eips::BlockNumberOrTag, - network::{BlockResponse, ReceiptResponse}, + network::{BlockResponse, ReceiptResponse, TransactionBuilder}, primitives::{Address, B256, U256}, - rpc::types::{BlockId, BlockTransactionsKind, Filter, FilterChanges, Log}, + rpc::types::{AccessListItem, BlockId, BlockTransactionsKind, Filter, FilterChanges, Log}, }; use axum::{ extract::{Path, State}, @@ -17,12 +17,14 @@ use axum::{ }; use axum_extra::extract::Query; use eyre::{eyre, OptionExt, Report, Result}; -use futures::future::try_join_all; +use futures::future::{join_all, try_join_all}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::json; use helios_common::network_spec::NetworkSpec; -use helios_core::execution::{proof::create_receipt_proof, rpc::ExecutionRpc}; +use helios_core::execution::{ + constants::PARALLEL_QUERY_BATCH_SIZE, proof::create_receipt_proof, rpc::ExecutionRpc, +}; use helios_verifiable_api_types::*; use crate::ApiState; @@ -180,6 +182,7 @@ pub async fn get_storage_at>( Query(BlockQuery { block }): Query, State(ApiState { rpc, .. }): State>, ) -> Response { + // ToDo(@eshaan7): ensure block number bcz multiple requests are being made let block = block.unwrap_or(BlockId::latest()); let storage_slot = rpc @@ -349,6 +352,87 @@ pub async fn get_filter_changes>( })) } +/// This method returns a list of all accounts (along with their proofs) that are accessed by a given transaction. +/// The list includes the `from`, `to` and `beneficiary` addresses as well. +/// +/// Replaces the `eth_createAccessList` RPC method. +pub async fn create_access_list>( + State(ApiState { rpc, .. }): State>, + Json(AccessListRequest { tx, block }): Json>, +) -> Response { + let block_id = block.unwrap_or(BlockId::latest()); + let block = match block_id { + BlockId::Number(number_or_tag) => rpc + .get_block_by_number(number_or_tag, BlockTransactionsKind::Hashes) + .await + .map_err(map_server_err)? + .ok_or_else(|| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + json_err("Block not found"), + ) + }), + BlockId::Hash(hash) => rpc.get_block(hash.into()).await.map_err(map_server_err), + }?; + let block_id = BlockId::Number(block.header().number().into()); + + let mut list = rpc + .create_access_list(&tx, block_id) + .await + .map_err(map_server_err)? + .0; + + let from_access_entry = AccessListItem { + address: tx.from().unwrap_or_default(), + storage_keys: Vec::default(), + }; + let to_access_entry = AccessListItem { + address: tx.to().unwrap_or_default(), + storage_keys: Vec::default(), + }; + let producer_access_entry = AccessListItem { + address: block.header().beneficiary(), + storage_keys: Vec::default(), + }; + + let list_addresses = list.iter().map(|elem| elem.address).collect::>(); + + if !list_addresses.contains(&from_access_entry.address) { + list.push(from_access_entry) + } + if !list_addresses.contains(&to_access_entry.address) { + list.push(to_access_entry) + } + if !list_addresses.contains(&producer_access_entry.address) { + list.push(producer_access_entry) + } + + let mut accounts = HashMap::new(); + for chunk in list.chunks(PARALLEL_QUERY_BATCH_SIZE) { + let account_chunk_futs = chunk.iter().map(|account| async { + let account_fut = get_account( + Path(account.address), + Query(AccountProofQuery { + storage_keys: account.storage_keys.clone(), + block: Some(block_id), + }), + State(ApiState::new_from_rpc(rpc.clone())), + ); + (account.address, account_fut.await) + }); + + let account_chunk = join_all(account_chunk_futs).await; + + account_chunk.into_iter().for_each(|(address, value)| { + if let Some(account) = value.ok() { + accounts.insert(address, account.0); + } + }); + } + + Ok(Json(AccessListResponse { accounts })) +} + async fn create_receipt_proofs_for_logs>( logs: &[Log], rpc: Arc, diff --git a/verifiable-api/server/bin/router.rs b/verifiable-api/server/bin/router.rs index dce0d46e..aa145fd8 100644 --- a/verifiable-api/server/bin/router.rs +++ b/verifiable-api/server/bin/router.rs @@ -1,4 +1,7 @@ -use axum::{routing::get, Router}; +use axum::{ + routing::{get, post}, + Router, +}; use helios_common::network_spec::NetworkSpec; use helios_core::execution::rpc::ExecutionRpc; @@ -21,6 +24,7 @@ pub fn build_router>() -> Router> { pub rpc: Arc, _marker: PhantomData, } +impl> Clone for ApiState { + fn clone(&self) -> Self { + Self { + rpc: self.rpc.clone(), + _marker: PhantomData::default(), + } + } +} + impl> ApiState { pub fn new(rpc: &str) -> Result { Ok(Self { @@ -19,4 +27,11 @@ impl> ApiState { _marker: PhantomData::default(), }) } + + pub fn new_from_rpc(rpc: Arc) -> Self { + Self { + rpc, + _marker: PhantomData::default(), + } + } } diff --git a/verifiable-api/types/src/lib.rs b/verifiable-api/types/src/lib.rs index f926aabe..662c834b 100644 --- a/verifiable-api/types/src/lib.rs +++ b/verifiable-api/types/src/lib.rs @@ -2,12 +2,13 @@ use std::collections::HashMap; use alloy::{ consensus::Account as TrieAccount, + eips::BlockId, primitives::{Address, Bytes, B256}, rpc::types::{EIP1186StorageProof, Log}, }; use serde::{Deserialize, Serialize}; -use helios_common::{network_spec::NetworkSpec, types::Account}; +use helios_common::network_spec::NetworkSpec; #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -56,7 +57,6 @@ pub struct FilterLogsResponse { #[serde(rename_all = "camelCase")] #[serde(bound = "N: NetworkSpec")] #[serde(untagged)] - pub enum FilterChangesResponse { Hashes(Vec), Logs(FilterLogsResponse), @@ -64,6 +64,13 @@ pub enum FilterChangesResponse { #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct CallResponse { - pub accounts: HashMap, +pub struct AccessListRequest { + pub tx: N::TransactionRequest, + pub block: Option, +} + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AccessListResponse { + pub accounts: HashMap, }