From cbfcbfbf7b297e146bd97728e478493e1cdf1771 Mon Sep 17 00:00:00 2001 From: Dan Cline <6798349+Rjected@users.noreply.github.com> Date: Fri, 31 Mar 2023 17:37:40 -0400 Subject: [PATCH] fix: add JsonStorageKey for eth_getProof and eth_getStorageAt (#2067) --- crates/primitives/src/serde_helper/mod.rs | 3 ++ .../src/serde_helper/storage_key.rs | 38 +++++++++++++++++++ crates/rpc/rpc-api/src/eth.rs | 7 ++-- crates/rpc/rpc-builder/tests/it/http.rs | 2 +- crates/rpc/rpc-types/src/eth/account.rs | 4 +- crates/rpc/rpc/src/eth/api/server.rs | 7 ++-- crates/rpc/rpc/src/eth/api/state.rs | 21 +++++----- 7 files changed, 64 insertions(+), 18 deletions(-) create mode 100644 crates/primitives/src/serde_helper/storage_key.rs diff --git a/crates/primitives/src/serde_helper/mod.rs b/crates/primitives/src/serde_helper/mod.rs index b2d4479787..d0bc14671c 100644 --- a/crates/primitives/src/serde_helper/mod.rs +++ b/crates/primitives/src/serde_helper/mod.rs @@ -1,5 +1,8 @@ //! Various serde utilities +mod storage_key; +pub use storage_key::*; + mod jsonu256; pub use jsonu256::*; diff --git a/crates/primitives/src/serde_helper/storage_key.rs b/crates/primitives/src/serde_helper/storage_key.rs new file mode 100644 index 0000000000..85826c259b --- /dev/null +++ b/crates/primitives/src/serde_helper/storage_key.rs @@ -0,0 +1,38 @@ +use crate::{H256, U256}; +use serde::{Deserialize, Serialize}; +use std::fmt::Write; + +/// A storage key type that can be serialized to and from a hex string up to 40 characters. Used +/// for `eth_getStorageAt` and `eth_getProof` RPCs. +#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[serde(from = "U256", into = "String")] +pub struct JsonStorageKey(pub H256); + +impl From for JsonStorageKey { + fn from(value: U256) -> Self { + // SAFETY: Address (H256) and U256 have the same number of bytes + JsonStorageKey(H256::from(value.to_be_bytes())) + } +} + +impl From for String { + fn from(value: JsonStorageKey) -> Self { + // SAFETY: Address (H256) and U256 have the same number of bytes + let uint = U256::from_be_bytes(value.0 .0); + + // serialize byte by byte + // + // this is mainly so we can return an output that hive testing expects, because the + // `eth_getProof` implementation in geth simply mirrors the input + // + // see the use of `hexKey` in the `eth_getProof` response: + // + let bytes = uint.to_be_bytes_trimmed_vec(); + let mut hex = String::with_capacity(2 + bytes.len() * 2); + hex.push_str("0x"); + for byte in bytes { + write!(hex, "{:02x}", byte).unwrap(); + } + hex + } +} diff --git a/crates/rpc/rpc-api/src/eth.rs b/crates/rpc/rpc-api/src/eth.rs index dc715088f6..c708d86cd9 100644 --- a/crates/rpc/rpc-api/src/eth.rs +++ b/crates/rpc/rpc-api/src/eth.rs @@ -1,6 +1,7 @@ use jsonrpsee::{core::RpcResult as Result, proc_macros::rpc}; use reth_primitives::{ - AccessListWithGasUsed, Address, BlockId, BlockNumberOrTag, Bytes, H256, H64, U256, U64, + serde_helper::JsonStorageKey, AccessListWithGasUsed, Address, BlockId, BlockNumberOrTag, Bytes, + H256, H64, U256, U64, }; use reth_rpc_types::{ state::StateOverride, CallRequest, EIP1186AccountProofResponse, FeeHistory, Index, RichBlock, @@ -116,7 +117,7 @@ pub trait EthApi { async fn storage_at( &self, address: Address, - index: U256, + index: JsonStorageKey, block_number: Option, ) -> Result; @@ -244,7 +245,7 @@ pub trait EthApi { async fn get_proof( &self, address: Address, - keys: Vec, + keys: Vec, block_number: Option, ) -> Result; } diff --git a/crates/rpc/rpc-builder/tests/it/http.rs b/crates/rpc/rpc-builder/tests/it/http.rs index 3f0c8b583b..0b6b9923d1 100644 --- a/crates/rpc/rpc-builder/tests/it/http.rs +++ b/crates/rpc/rpc-builder/tests/it/http.rs @@ -68,7 +68,7 @@ where EthApiClient::fee_history(client, 0.into(), block_number.into(), None).await.unwrap(); EthApiClient::balance(client, address, None).await.unwrap(); EthApiClient::transaction_count(client, address, None).await.unwrap(); - EthApiClient::storage_at(client, address, U256::default(), None).await.unwrap(); + EthApiClient::storage_at(client, address, U256::default().into(), None).await.unwrap(); EthApiClient::block_by_hash(client, hash, false).await.unwrap(); EthApiClient::block_by_number(client, block_number, false).await.unwrap(); EthApiClient::block_transaction_count_by_number(client, block_number).await.unwrap(); diff --git a/crates/rpc/rpc-types/src/eth/account.rs b/crates/rpc/rpc-types/src/eth/account.rs index 2086920b23..a863d1c4d3 100644 --- a/crates/rpc/rpc-types/src/eth/account.rs +++ b/crates/rpc/rpc-types/src/eth/account.rs @@ -1,5 +1,5 @@ #![allow(missing_docs)] -use reth_primitives::{Address, Bytes, H256, H512, U256, U64}; +use reth_primitives::{serde_helper::JsonStorageKey, Address, Bytes, H256, H512, U256, U64}; use serde::{Deserialize, Serialize}; /// Account information. @@ -14,7 +14,7 @@ pub struct AccountInfo { #[serde(rename_all = "camelCase")] pub struct StorageProof { /// Storage key. - pub key: U256, + pub key: JsonStorageKey, /// Value that the key holds pub value: U256, /// proof for the pair diff --git a/crates/rpc/rpc/src/eth/api/server.rs b/crates/rpc/rpc/src/eth/api/server.rs index eba99f95d2..8a85776529 100644 --- a/crates/rpc/rpc/src/eth/api/server.rs +++ b/crates/rpc/rpc/src/eth/api/server.rs @@ -11,7 +11,8 @@ use crate::{ }; use jsonrpsee::core::RpcResult as Result; use reth_primitives::{ - AccessListWithGasUsed, Address, BlockId, BlockNumberOrTag, Bytes, Header, H256, H64, U256, U64, + serde_helper::JsonStorageKey, AccessListWithGasUsed, Address, BlockId, BlockNumberOrTag, Bytes, + Header, H256, H64, U256, U64, }; use reth_provider::{BlockProvider, EvmEnvProvider, HeaderProvider, StateProviderFactory}; use reth_rpc_api::EthApiServer; @@ -176,7 +177,7 @@ where async fn storage_at( &self, address: Address, - index: U256, + index: JsonStorageKey, block_number: Option, ) -> Result { trace!(target: "rpc::eth", ?address, ?block_number, "Serving eth_getStorageAt"); @@ -421,7 +422,7 @@ where async fn get_proof( &self, address: Address, - keys: Vec, + keys: Vec, block_number: Option, ) -> Result { trace!(target: "rpc::eth", ?address, ?keys, ?block_number, "Serving eth_getProof"); diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 03622bd410..9aadb79c16 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -4,7 +4,10 @@ use crate::{ eth::error::{EthApiError, EthResult}, EthApi, }; -use reth_primitives::{Address, BlockId, BlockNumberOrTag, Bytes, H256, KECCAK_EMPTY, U256}; +use reth_primitives::{ + serde_helper::JsonStorageKey, Address, BlockId, BlockNumberOrTag, Bytes, H256, KECCAK_EMPTY, + U256, +}; use reth_provider::{ AccountProvider, BlockProvider, EvmEnvProvider, StateProvider, StateProviderFactory, }; @@ -39,19 +42,18 @@ where pub(crate) fn storage_at( &self, address: Address, - index: U256, + index: JsonStorageKey, block_id: Option, ) -> EthResult { let state = self.state_at_block_id_or_latest(block_id)?; - let storage_key = H256(index.to_be_bytes()); - let value = state.storage(address, storage_key)?.unwrap_or_default(); + let value = state.storage(address, index.0)?.unwrap_or_default(); Ok(H256(value.to_be_bytes())) } pub(crate) fn get_proof( &self, address: Address, - keys: Vec, + keys: Vec, block_id: Option, ) -> EthResult { let chain_info = self.client().chain_info()?; @@ -74,14 +76,15 @@ where let state = self.state_at_block_id(block_id)?; - let (account_proof, storage_hash, stg_proofs) = state.proof(address, &keys)?; + let hash_keys = keys.iter().map(|key| key.0).collect::>(); + let (account_proof, storage_hash, stg_proofs) = state.proof(address, &hash_keys)?; let storage_proof = keys .into_iter() .zip(stg_proofs) .map(|(key, proof)| { - state.storage(address, key).map(|op| StorageProof { - key: U256::from_be_bytes(*key.as_fixed_bytes()), + state.storage(address, key.0).map(|op| StorageProof { + key, value: op.unwrap_or_default(), proof, }) @@ -97,7 +100,7 @@ where ..Default::default() }; - if let Some(account) = state.basic_account(address)? { + if let Some(account) = state.basic_account(proof.address)? { proof.balance = account.balance; proof.nonce = account.nonce.into(); proof.code_hash = account.get_bytecode_hash();