From aa7f092fe4e22fdc7bcec43c583f680272ef7bfb Mon Sep 17 00:00:00 2001 From: Eshaan Bansal Date: Fri, 7 Feb 2025 16:23:20 +0530 Subject: [PATCH] impl more methods --- core/src/execution/mod.rs | 9 +-- core/src/execution/rpc/http_rpc.rs | 11 +--- core/src/execution/rpc/mock_rpc.rs | 5 +- core/src/execution/rpc/mod.rs | 2 +- verifiable-api/README.md | 5 +- verifiable-api/bin/handlers.rs | 94 ++++++++++++++++++++++++++++-- verifiable-api/bin/router.rs | 8 ++- verifiable-api/src/api_client.rs | 52 +++++++++++++++++ verifiable-api/src/types.rs | 19 +++++- 9 files changed, 176 insertions(+), 29 deletions(-) diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index 6f41527e..94bcd10b 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -1,6 +1,7 @@ use std::collections::{HashMap, HashSet}; use alloy::consensus::BlockHeader; +use alloy::eips::BlockId; use alloy::network::primitives::HeaderResponse; use alloy::network::{BlockResponse, ReceiptResponse}; use alloy::primitives::{keccak256, Address, B256, U256}; @@ -206,6 +207,7 @@ impl> ExecutionClient { let receipt = receipt.unwrap(); let block_number = receipt.block_number().unwrap(); + let block_id = BlockId::from(block_number); let tag = BlockTag::Number(block_number); let block = self.state.get_block(tag).await; @@ -218,7 +220,7 @@ impl> ExecutionClient { // Fetch all receipts in block, check root and inclusion let receipts = self .rpc - .get_block_receipts(tag) + .get_block_receipts(block_id) .await? .ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?; @@ -251,12 +253,11 @@ impl> ExecutionClient { } else { return Ok(None); }; - - let tag = BlockTag::Number(block.header().number()); + let block_id = BlockId::from(block.header().number()); let receipts = self .rpc - .get_block_receipts(tag) + .get_block_receipts(block_id) .await? .ok_or(eyre::eyre!(ExecutionError::NoReceiptsForBlock(tag)))?; diff --git a/core/src/execution/rpc/http_rpc.rs b/core/src/execution/rpc/http_rpc.rs index ef6c0237..d2146810 100644 --- a/core/src/execution/rpc/http_rpc.rs +++ b/core/src/execution/rpc/http_rpc.rs @@ -121,17 +121,10 @@ impl ExecutionRpc for HttpRpc { Ok(receipt) } - async fn get_block_receipts(&self, block: BlockTag) -> Result>> { - let block = match block { - BlockTag::Latest => BlockNumberOrTag::Latest, - BlockTag::Finalized => BlockNumberOrTag::Finalized, - BlockTag::Number(num) => BlockNumberOrTag::Number(num), - }; - - let block_id = BlockId::from(block); + async fn get_block_receipts(&self, block: BlockId) -> Result>> { let receipts = self .provider - .get_block_receipts(block_id) + .get_block_receipts(block) .await .map_err(|e| RpcError::new("get_block_receipts", e))?; diff --git a/core/src/execution/rpc/mock_rpc.rs b/core/src/execution/rpc/mock_rpc.rs index 95b12b11..cc6f8518 100644 --- a/core/src/execution/rpc/mock_rpc.rs +++ b/core/src/execution/rpc/mock_rpc.rs @@ -58,10 +58,7 @@ impl ExecutionRpc for MockRpc { Ok(serde_json::from_str(&receipt)?) } - async fn get_block_receipts( - &self, - _block: BlockTag, - ) -> Result>> { + async fn get_block_receipts(&self, _block: BlockId) -> Result>> { let receipts = read_to_string(self.path.join("receipts.json"))?; Ok(serde_json::from_str(&receipts)?) } diff --git a/core/src/execution/rpc/mod.rs b/core/src/execution/rpc/mod.rs index 6675bf6d..6cc2e552 100644 --- a/core/src/execution/rpc/mod.rs +++ b/core/src/execution/rpc/mod.rs @@ -36,7 +36,7 @@ pub trait ExecutionRpc: Send + Clone + Sync + 'static { async fn get_code(&self, address: Address, block: u64) -> Result>; async fn send_raw_transaction(&self, bytes: &[u8]) -> Result; async fn get_transaction_receipt(&self, tx_hash: B256) -> Result>; - async fn get_block_receipts(&self, block: BlockTag) -> Result>>; + async fn get_block_receipts(&self, block: BlockId) -> Result>>; async fn get_transaction(&self, tx_hash: B256) -> Result>; async fn get_logs(&self, filter: &Filter) -> Result>; async fn get_filter_changes(&self, filter_id: U256) -> Result; diff --git a/verifiable-api/README.md b/verifiable-api/README.md index 9c1d0bc0..6874aa38 100644 --- a/verifiable-api/README.md +++ b/verifiable-api/README.md @@ -4,9 +4,12 @@ | Ethereum JSON-RPC Method | Helios Verifiable API Endpoint | |--------------------------------|-------------------------------------------------------------------------| +| `eth_getProof` | `/eth/v1/proof/account/{address}?block={tag_or_hash_number}` | | `eth_getBalance` | `/eth/v1/proof/balance/{address}?block={tag_or_hash_number}` | | `eth_getTransactionCount` | `/eth/v1/proof/transaction_count/{address}?block={tag_or_hash_number}` | | `eth_getCode` | `/eth/v1/proof/code/{address}?block={tag_or_hash_number}` | | `eth_getStorageAt` | `/eth/v1/proof/storage/{address}/{slot}?block={tag_or_hash_number}` | +| `eth_getBlockReceipts` | `/eth/v1/proof/block_receipts/{block}` | | `eth_getTransactionReceipt` | `/eth/v1/proof/tx_receipt/{tx_hash}` | -| `eth_getFilterLogs` | `/eth/v1/proof/filter_logs/{filter_id}` | \ No newline at end of file +| `eth_getFilterLogs` | `/eth/v1/proof/filter_logs/{filter_id}` | +| `eth_getFilterChanges` | `/eth/v1/proof/filter_changes/{filter_id}` | diff --git a/verifiable-api/bin/handlers.rs b/verifiable-api/bin/handlers.rs index 073cfd70..4c9f8a6a 100644 --- a/verifiable-api/bin/handlers.rs +++ b/verifiable-api/bin/handlers.rs @@ -8,7 +8,7 @@ use alloy::{ eips::BlockNumberOrTag, network::{BlockResponse, ReceiptResponse}, primitives::{Address, B256, U256}, - rpc::types::{BlockId, BlockTransactionsKind, Log}, + rpc::types::{BlockId, BlockTransactionsKind, FilterChanges, Log}, }; use axum::{ extract::{Path, Query, State}, @@ -20,7 +20,7 @@ use futures::future::try_join_all; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_json::json; -use helios_core::{execution::rpc::ExecutionRpc, network_spec::NetworkSpec, types::BlockTag}; +use helios_core::{execution::rpc::ExecutionRpc, network_spec::NetworkSpec}; use helios_verifiable_api::{proof::create_receipt_proof, rpc_client::ExecutionClient, types::*}; use crate::ApiState; @@ -42,6 +42,34 @@ pub struct BlockQuery { block: Option, } +#[derive(Deserialize)] +pub struct AccountProofQuery { + pub block: Option, + pub storage_keys: Vec, +} + +/// This method returns the account proof for a given address. +/// +/// Replaces the `eth_getProof` RPC method. +pub async fn get_account_proof>( + Path(address): Path
, + Query(AccountProofQuery { + block, + storage_keys, + }): Query, + State(ApiState { execution_client }): State>, +) -> Response { + let block = block.unwrap_or(BlockId::latest()); + + let proof = execution_client + .rpc + .get_proof(address, &storage_keys, block) + .await + .map_err(map_server_err)?; + + Ok(Json(proof)) +} + /// This method returns the balance of an account for a given address, /// along with the Merkle proof of the account's inclusion in the state trie. /// @@ -178,7 +206,7 @@ pub async fn get_storage_at>( let proof = execution_client .rpc - .get_proof(address, &[storage_slot.into()], block) + .get_proof(address, &[storage_slot], block) .await .map_err(map_server_err)?; @@ -204,6 +232,25 @@ pub async fn get_storage_at>( })) } +/// This method returns all transaction receipts for a given block. +/// +/// Replaces the `eth_getBlockReceipts` RPC method. +pub async fn get_block_receipts>( + Path(block): Path>, + State(ApiState { execution_client }): State>, +) -> Response> { + let block = block.unwrap_or(BlockId::latest()); + + let receipts = execution_client + .rpc + .get_block_receipts(block) + .await + .map_err(map_server_err)? + .unwrap_or(vec![]); + + Ok(Json(receipts)) +} + /// This method returns the receipt of a transaction along with a Merkle proof of its inclusion. /// /// Replaces the `eth_getTransactionReceipt` RPC method. @@ -225,7 +272,7 @@ pub async fn get_transaction_receipt>( let receipts = execution_client .rpc - .get_block_receipts(BlockTag::Number(receipt.block_number().unwrap())) + .get_block_receipts(BlockId::from(receipt.block_number().unwrap())) .await .map_err(map_server_err)? .ok_or_else(|| { @@ -270,6 +317,41 @@ pub async fn get_filter_logs>( })) } +/// This method returns the changes since the last poll for a given filter id. +/// If filter is of logs type, then corresponding to each log, +/// it also returns the transaction receipt and a Merkle proof of its inclusion.. +/// +/// Replaces the `eth_getFilterChanges` RPC method. +pub async fn get_filter_changes>( + Path(filter_id): Path, + State(ApiState { execution_client }): State>, +) -> Response> { + let filter_changes = execution_client + .rpc + .get_filter_changes(filter_id) + .await + .map_err(map_server_err)?; + + Ok(Json(match filter_changes { + FilterChanges::Logs(logs) => { + // Create receipt proofs for each log + let receipt_proofs = create_receipt_proofs_for_logs(&logs, execution_client) + .await + .map_err(map_server_err)?; + + GetFilterChangesResponse::Logs(GetFilterLogsResponse { + logs, + receipt_proofs, + }) + } + FilterChanges::Hashes(hashes) => GetFilterChangesResponse::Hashes(hashes), + FilterChanges::Empty => GetFilterChangesResponse::Hashes(vec![]), + FilterChanges::Transactions(txs) => GetFilterChangesResponse::Hashes( + txs.into_iter().map(|t| t.inner.tx_hash().clone()).collect(), + ), + })) +} + async fn create_receipt_proofs_for_logs>( logs: &[Log], execution_client: Arc>, @@ -284,8 +366,8 @@ async fn create_receipt_proofs_for_logs>( let blocks_receipts_fut = block_nums.into_iter().map(|block_num| { let execution_client = Arc::clone(&execution_client); async move { - let tag = BlockTag::Number(block_num); - let receipts = execution_client.rpc.get_block_receipts(tag).await?; + let block_id = BlockId::from(block_num); + let receipts = execution_client.rpc.get_block_receipts(block_id).await?; receipts .ok_or_eyre("No receipts found for the block") .map(|receipts| (block_num, receipts)) diff --git a/verifiable-api/bin/router.rs b/verifiable-api/bin/router.rs index 9f02a052..a94f828f 100644 --- a/verifiable-api/bin/router.rs +++ b/verifiable-api/bin/router.rs @@ -8,6 +8,7 @@ pub fn build_router>() -> Router>() -> Router { + async fn get_account( + &self, + address: Address, + block: Option, + ) -> Result; async fn get_balance( &self, address: Address, @@ -33,11 +38,19 @@ pub trait VerifiableApi { key: U256, block: Option, ) -> Result; + async fn get_block_receipts( + &self, + block: BlockId, + ) -> Result, Error>; async fn get_transaction_receipt( &self, tx_hash: B256, ) -> Result, Error>; async fn get_filter_logs(&self, filter_id: U256) -> Result, Error>; + async fn get_filter_changes( + &self, + filter_id: U256, + ) -> Result, Error>; } pub struct VerifiableApiClient { @@ -56,6 +69,22 @@ impl VerifiableApiClient { #[async_trait] impl VerifiableApi for VerifiableApiClient { + async fn get_account( + &self, + address: Address, + block: Option, + ) -> Result { + let url = format!("{}/eth/v1/proof/account/{}", self.base_url, address); + let response = self + .client + .get(&url) + .query(&[("block", block)]) + .send() + .await?; + let response = response.json::().await?; + Ok(response) + } + async fn get_balance( &self, address: Address, @@ -124,6 +153,16 @@ impl VerifiableApi for VerifiableApiClient { Ok(response) } + async fn get_block_receipts( + &self, + block: BlockId, + ) -> Result, Error> { + let url = format!("{}/eth/v1/proof/block_receipts/{}", self.base_url, block); + let response = self.client.get(&url).send().await?; + let response = response.json::>().await?; + Ok(response) + } + async fn get_transaction_receipt( &self, tx_hash: B256, @@ -140,4 +179,17 @@ impl VerifiableApi for VerifiableApiClient { let response = response.json::>().await?; Ok(response) } + + async fn get_filter_changes( + &self, + filter_id: U256, + ) -> Result, Error> { + let url = format!( + "{}/eth/v1/proof/filter_changes/{}", + self.base_url, filter_id + ); + let response = self.client.get(&url).send().await?; + let response = response.json::>().await?; + Ok(response) + } } diff --git a/verifiable-api/src/types.rs b/verifiable-api/src/types.rs index c15b7049..edb3a5d3 100644 --- a/verifiable-api/src/types.rs +++ b/verifiable-api/src/types.rs @@ -3,12 +3,13 @@ use std::collections::HashMap; use alloy::{ consensus::Account as TrieAccount, primitives::{Address, Bytes, B256}, - rpc::types::{EIP1186StorageProof, Log}, + rpc::types::{EIP1186AccountProofResponse, EIP1186StorageProof, Log}, }; use serde::{Deserialize, Serialize}; use helios_core::{execution::types::Account, network_spec::NetworkSpec}; +pub type GetAccountProofResponse = EIP1186AccountProofResponse; #[derive(Serialize, Deserialize)] pub struct GetBalanceResponse { pub account: TrieAccount, @@ -35,6 +36,9 @@ pub struct GetStorageAtResponse { pub account_proof: Vec, } +#[allow(type_alias_bounds)] +pub type GetBlockReceiptsResponse = Vec; + #[derive(Serialize, Deserialize)] pub struct GetTransactionReceiptResponse { pub receipt: N::ReceiptResponse, @@ -44,14 +48,23 @@ pub struct GetTransactionReceiptResponse { #[derive(Serialize, Deserialize)] #[serde(bound = "N: NetworkSpec")] pub struct GetLogsResponse { - pub receipt_proofs: HashMap>, // tx_hash -> receipt + pub receipt_proofs: HashMap>, // tx_hash -> receipt & proof } #[derive(Serialize, Deserialize)] #[serde(bound = "N: NetworkSpec")] pub struct GetFilterLogsResponse { pub logs: Vec, - pub receipt_proofs: HashMap>, // tx_hash -> receipt + pub receipt_proofs: HashMap>, // tx_hash -> receipt & proof +} + +#[derive(Serialize, Deserialize)] +#[serde(bound = "N: NetworkSpec")] +#[serde(untagged)] + +pub enum GetFilterChangesResponse { + Hashes(Vec), + Logs(GetFilterLogsResponse), } #[derive(Serialize, Deserialize)]