-
Notifications
You must be signed in to change notification settings - Fork 10
feat: add support for eth_getProof
#257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| //! OP-Reth `eth_` endpoint implementation. | ||
|
|
||
| pub mod ext; | ||
| pub mod proofs; | ||
| pub mod receipt; | ||
| pub mod transaction; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||||||||||||||
| //! Historical proofs RPC server implementation. | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| use alloy_eips::BlockId; | ||||||||||||||||||||||
| use alloy_primitives::Address; | ||||||||||||||||||||||
| use alloy_rpc_types_eth::EIP1186AccountProofResponse; | ||||||||||||||||||||||
| use alloy_serde::JsonStorageKey; | ||||||||||||||||||||||
| use async_trait::async_trait; | ||||||||||||||||||||||
| use derive_more::Constructor; | ||||||||||||||||||||||
| use jsonrpsee::proc_macros::rpc; | ||||||||||||||||||||||
| use jsonrpsee_core::RpcResult; | ||||||||||||||||||||||
| use jsonrpsee_types::error::ErrorObject; | ||||||||||||||||||||||
| use reth_optimism_trie::{provider::OpProofsStateProviderRef, OpProofsStorage}; | ||||||||||||||||||||||
| use reth_provider::{BlockIdReader, ProviderError, ProviderResult, StateProofProvider}; | ||||||||||||||||||||||
| use reth_rpc_api::eth::helpers::FullEthApi; | ||||||||||||||||||||||
| use reth_rpc_eth_types::EthApiError; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #[cfg_attr(not(test), rpc(server, namespace = "eth"))] | ||||||||||||||||||||||
| #[cfg_attr(test, rpc(server, client, namespace = "eth"))] | ||||||||||||||||||||||
| pub trait EthApiOverride { | ||||||||||||||||||||||
| /// Returns the account and storage values of the specified account including the Merkle-proof. | ||||||||||||||||||||||
| /// This call can be used to verify that the data you are pulling from is not tampered with. | ||||||||||||||||||||||
| #[method(name = "getProof")] | ||||||||||||||||||||||
| async fn get_proof( | ||||||||||||||||||||||
| &self, | ||||||||||||||||||||||
| address: Address, | ||||||||||||||||||||||
| keys: Vec<JsonStorageKey>, | ||||||||||||||||||||||
| block_number: Option<BlockId>, | ||||||||||||||||||||||
| ) -> RpcResult<EIP1186AccountProofResponse>; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #[derive(Debug, Constructor)] | ||||||||||||||||||||||
| /// Overrides applied to the `eth_` namespace of the RPC API for historical proofs ExEx. | ||||||||||||||||||||||
| pub struct EthApiExt<Eth, P> { | ||||||||||||||||||||||
| eth_api: Eth, | ||||||||||||||||||||||
| preimage_store: P, | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| impl<Eth, P> EthApiExt<Eth, P> | ||||||||||||||||||||||
| where | ||||||||||||||||||||||
| Eth: FullEthApi + Send + Sync + 'static, | ||||||||||||||||||||||
| ErrorObject<'static>: From<Eth::Error>, | ||||||||||||||||||||||
| P: OpProofsStorage + Clone + 'static, | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| async fn state_provider( | ||||||||||||||||||||||
| &self, | ||||||||||||||||||||||
| block_id: Option<BlockId>, | ||||||||||||||||||||||
| ) -> ProviderResult<impl StateProofProvider> { | ||||||||||||||||||||||
| let block_id = block_id.unwrap_or_default(); | ||||||||||||||||||||||
| // Check whether the distance to the block exceeds the maximum configured window. | ||||||||||||||||||||||
| let block_number = self | ||||||||||||||||||||||
| .eth_api | ||||||||||||||||||||||
| .provider() | ||||||||||||||||||||||
| .block_number_for_id(block_id)? | ||||||||||||||||||||||
| .ok_or(EthApiError::HeaderNotFound(block_id)) | ||||||||||||||||||||||
| .map_err(ProviderError::other)?; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let historical_provider = | ||||||||||||||||||||||
| self.eth_api.state_at_block_id(block_id).await.map_err(ProviderError::other)?; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let (Some((latest_block_number, _)), Some((earliest_block_number, _))) = ( | ||||||||||||||||||||||
| self.preimage_store | ||||||||||||||||||||||
| .get_latest_block_number() | ||||||||||||||||||||||
| .await | ||||||||||||||||||||||
| .map_err(|e| ProviderError::Database(e.into()))?, | ||||||||||||||||||||||
| self.preimage_store | ||||||||||||||||||||||
| .get_earliest_block_number() | ||||||||||||||||||||||
| .await | ||||||||||||||||||||||
| .map_err(|e| ProviderError::Database(e.into()))?, | ||||||||||||||||||||||
| ) else { | ||||||||||||||||||||||
| // if no earliest block, db is empty - use historical provider | ||||||||||||||||||||||
| return Ok(historical_provider as Box<dyn StateProofProvider>); | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if block_number < earliest_block_number || block_number > latest_block_number { | ||||||||||||||||||||||
| return Ok(historical_provider as Box<dyn StateProofProvider>); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let external_overlay_provider = | ||||||||||||||||||||||
| OpProofsStateProviderRef::new(historical_provider, &self.preimage_store, block_number); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Ok(Box::new(external_overlay_provider)) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| #[async_trait] | ||||||||||||||||||||||
meyer9 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||
| impl<Eth, P> EthApiOverrideServer for EthApiExt<Eth, P> | ||||||||||||||||||||||
| where | ||||||||||||||||||||||
| Eth: FullEthApi + Send + Sync + 'static, | ||||||||||||||||||||||
| ErrorObject<'static>: From<Eth::Error>, | ||||||||||||||||||||||
| P: OpProofsStorage + Clone + 'static, | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| async fn get_proof( | ||||||||||||||||||||||
| &self, | ||||||||||||||||||||||
| address: Address, | ||||||||||||||||||||||
| keys: Vec<JsonStorageKey>, | ||||||||||||||||||||||
| block_number: Option<BlockId>, | ||||||||||||||||||||||
| ) -> RpcResult<EIP1186AccountProofResponse> { | ||||||||||||||||||||||
| let state = self.state_provider(block_number).await.map_err(Into::into)?; | ||||||||||||||||||||||
| let storage_keys = keys.iter().map(|key| key.as_b256()).collect::<Vec<_>>(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| let proof = state.proof(Default::default(), address, &storage_keys).map_err(Into::into)?; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
Comment on lines
+101
to
+102
|
||||||||||||||||||||||
| let proof = state.proof(Default::default(), address, &storage_keys).map_err(Into::into)?; | |
| // Fetch the block header to get the correct state root | |
| let block_id = block_number.unwrap_or_default(); | |
| let header = self.eth_api.provider().header_by_id(block_id) | |
| .map_err(Into::into)? | |
| .ok_or(EthApiError::HeaderNotFound(block_id)) | |
| .map_err(Into::into)?; | |
| let state_root = header.state_root; | |
| let proof = state.proof(state_root, address, &storage_keys).map_err(Into::into)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this suggestion..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@itschaindev Default::default() is not the state root - it's the hashed post state which is the state we apply on top of the current state, not the current state root (I think Copilot is hallucinating, or doesn't have enough context). We don't want any state applied on top of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this particular design we need to initialize Proof Storage regardless of proof history is enabled or not. Because we are checking the
proofs_history_enabledinside this function.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you pls open a debt issue for improving this design permalinking to this code @sadiq1971