diff --git a/src/client/client.rs b/src/client/client.rs index 8945dd1..f935c43 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -41,6 +41,11 @@ impl Client { Ok(account.nonce) } + pub async fn get_code(&mut self, address: Address) -> Result> { + let payload = self.consensus.get_execution_payload().await?; + self.execution.get_code(&address, &payload).await + } + pub fn get_header(&self) -> &Header { self.consensus.get_head() } diff --git a/src/common/utils.rs b/src/common/utils.rs index 6e9d8e4..9c0794f 100644 --- a/src/common/utils.rs +++ b/src/common/utils.rs @@ -1,3 +1,4 @@ +use ethers::prelude::Address; use eyre::Result; use ssz_rs::{Node, Vector}; @@ -15,3 +16,11 @@ pub fn bytes32_to_node(bytes: &Bytes32) -> Result { pub fn bytes_to_bytes32(bytes: &[u8]) -> Bytes32 { Vector::from_iter(bytes.to_vec()) } + +pub fn address_to_hex_string(address: &Address) -> String { + format!("0x{}", hex::encode(address.as_bytes())) +} + +pub fn u64_to_hex_string(val: u64) -> String { + format!("0x{:x}", val) +} diff --git a/src/consensus/consensus.rs b/src/consensus/consensus.rs index 92d5aa6..d4ee6d5 100644 --- a/src/consensus/consensus.rs +++ b/src/consensus/consensus.rs @@ -3,9 +3,9 @@ use blst::BLST_ERROR; use eyre::Result; use ssz_rs::prelude::*; -use crate::common::utils::*; -use super::types::*; use super::rpc::Rpc; +use super::types::*; +use crate::common::utils::*; pub struct ConsensusClient { rpc: Rpc, @@ -44,10 +44,7 @@ impl ConsensusClient { next_sync_committee: None, }; - Ok(ConsensusClient { - rpc, - store, - }) + Ok(ConsensusClient { rpc, store }) } pub async fn get_execution_payload(&mut self) -> Result { diff --git a/src/consensus/rpc.rs b/src/consensus/rpc.rs index 80bd2a0..10d5625 100644 --- a/src/consensus/rpc.rs +++ b/src/consensus/rpc.rs @@ -8,7 +8,9 @@ pub struct Rpc { impl Rpc { pub fn new(rpc: &str) -> Self { - Rpc { rpc: rpc.to_string() } + Rpc { + rpc: rpc.to_string(), + } } pub async fn get_bootstrap(&self, block_root: &str) -> Result { @@ -77,4 +79,3 @@ struct BootstrapResponse { struct BootstrapData { v: Bootstrap, } - diff --git a/src/execution/execution.rs b/src/execution/execution.rs index 6a2a93f..9fcb699 100644 --- a/src/execution/execution.rs +++ b/src/execution/execution.rs @@ -1,10 +1,10 @@ -use ethers::prelude::{Address, U256, H256}; +use ethers::prelude::{Address, H256, U256}; use ethers::utils::keccak256; use eyre::Result; -use crate::consensus::types::ExecutionPayload; use super::proof::{encode_account, verify_proof}; use super::rpc::Rpc; +use crate::consensus::types::ExecutionPayload; pub struct ExecutionClient { rpc: Rpc, @@ -16,11 +16,12 @@ impl ExecutionClient { ExecutionClient { rpc } } - pub async fn get_account(&self, address: &Address, payload: &ExecutionPayload) -> Result { - let proof = self - .rpc - .get_proof(&address, payload.block_number) - .await?; + pub async fn get_account( + &self, + address: &Address, + payload: &ExecutionPayload, + ) -> Result { + let proof = self.rpc.get_proof(&address, payload.block_number).await?; let account_path = keccak256(address.as_bytes()).to_vec(); let account_encoded = encode_account(&proof); @@ -43,6 +44,19 @@ impl ExecutionClient { storage_hash: proof.storage_hash, }) } + + pub async fn get_code(&self, address: &Address, payload: &ExecutionPayload) -> Result> { + let account = self.get_account(address, payload).await?; + let code = self.rpc.get_code(address, payload.block_number).await?; + + let code_hash = keccak256(&code).into(); + + if account.code_hash != code_hash { + eyre::bail!("Invalid Proof"); + } + + Ok(code) + } } pub struct Account { diff --git a/src/execution/mod.rs b/src/execution/mod.rs index 06f7a54..cd0f3c4 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -3,5 +3,5 @@ pub mod types; mod execution; pub use execution::*; -mod rpc; mod proof; +mod rpc; diff --git a/src/execution/rpc.rs b/src/execution/rpc.rs index 48cf375..1aae288 100644 --- a/src/execution/rpc.rs +++ b/src/execution/rpc.rs @@ -1,6 +1,12 @@ use ethers::prelude::Address; use eyre::Result; -use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params}; +use jsonrpsee::{ + core::client::ClientT, + http_client::{HttpClient, HttpClientBuilder}, + rpc_params, +}; + +use crate::common::utils::{address_to_hex_string, hex_str_to_bytes, u64_to_hex_string}; use super::types::Proof; @@ -10,15 +16,29 @@ pub struct Rpc { impl Rpc { pub fn new(rpc: &str) -> Self { - Rpc { rpc: rpc.to_string() } + Rpc { + rpc: rpc.to_string(), + } } pub async fn get_proof(&self, address: &Address, block: u64) -> Result { - let client = HttpClientBuilder::default().build(&self.rpc)?; - let block_hex = format!("0x{:x}", block); - let addr_hex = format!("0x{}", hex::encode(address.as_bytes())); + let client = self.client()?; + let block_hex = u64_to_hex_string(block); + let addr_hex = address_to_hex_string(address); let params = rpc_params!(addr_hex, [""], block_hex); Ok(client.request("eth_getProof", params).await?) } -} + pub async fn get_code(&self, address: &Address, block: u64) -> Result> { + let client = self.client()?; + let block_hex = u64_to_hex_string(block); + let addr_hex = address_to_hex_string(address); + let params = rpc_params!(addr_hex, block_hex); + let code: String = client.request("eth_getCode", params).await?; + hex_str_to_bytes(&code) + } + + fn client(&self) -> Result { + Ok(HttpClientBuilder::default().build(&self.rpc)?) + } +} diff --git a/src/execution/types.rs b/src/execution/types.rs index 7484eae..a21a83e 100644 --- a/src/execution/types.rs +++ b/src/execution/types.rs @@ -1,7 +1,7 @@ +use ethers::prelude::{Address, H256, U256}; +use eyre::Result; use serde::de::Error; use serde::Deserialize; -use ethers::prelude::{U256, H256, Address}; -use eyre::Result; use crate::common::utils::hex_str_to_bytes; diff --git a/src/main.rs b/src/main.rs index 15b7768..50a9f6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,9 @@ use eyre::Result; use client::Client; pub mod client; +pub mod common; pub mod consensus; pub mod execution; -pub mod common; #[tokio::main] async fn main() -> Result<()> { @@ -22,11 +22,14 @@ async fn main() -> Result<()> { let header = client.get_header(); println!("synced up to slot: {}", header.slot); - let address = Address::from_str("0xe0Fa62CD8543473627D337fAe1212d4E639EE932")?; + let address = Address::from_str("0x14f9D4aF749609c1438528C0Cce1cC3f6D411c47")?; let balance = client.get_balance(address).await?; let nonce = client.get_nonce(address).await?; + let code = client.get_code(address).await?; + println!("balance: {}", balance); println!("nonce: {}", nonce); + println!("code: 0x{}...", hex::encode(code[..5].to_vec())); Ok(()) }