From e18aa704e19557862c6cd695a528aec0e04757cb Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Thu, 25 Aug 2022 21:18:47 -0400 Subject: [PATCH] expose basic rpc api --- src/client/client.rs | 18 +++--- src/client/mod.rs | 2 + src/client/rpc.rs | 128 +++++++++++++++++++++++++++++++++++++ src/consensus/consensus.rs | 6 +- src/consensus/types.rs | 16 ++--- src/execution/evm.rs | 64 +++++++++++++------ src/execution/execution.rs | 1 + src/execution/mod.rs | 2 +- src/execution/types.rs | 2 +- src/main.rs | 29 ++------- 10 files changed, 206 insertions(+), 62 deletions(-) create mode 100644 src/client/rpc.rs diff --git a/src/client/client.rs b/src/client/client.rs index c9b4fd8..12e15a0 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1,14 +1,16 @@ +use std::sync::Arc; + use ethers::prelude::{Address, U256}; use eyre::Result; use crate::consensus::types::Header; use crate::consensus::ConsensusClient; -use crate::execution::ExecutionClient; use crate::execution::evm::Evm; +use crate::execution::ExecutionClient; pub struct Client { consensus: ConsensusClient, - execution: ExecutionClient, + execution: Arc, } impl Client { @@ -22,7 +24,7 @@ impl Client { Ok(Client { consensus, - execution, + execution: Arc::new(execution), }) } @@ -30,30 +32,30 @@ impl Client { self.consensus.sync().await } - pub async fn call(&mut self, to: &Address, calldata: &Vec, value: U256) -> Result> { + pub async fn call(&self, to: &Address, calldata: &Vec, value: U256) -> Result> { let payload = self.consensus.get_execution_payload().await?; let mut evm = Evm::new(self.execution.clone(), payload); evm.call(to, calldata, value) } - pub async fn get_balance(&mut self, address: &Address) -> Result { + pub async fn get_balance(&self, address: &Address) -> Result { let payload = self.consensus.get_execution_payload().await?; let account = self.execution.get_account(&address, None, &payload).await?; Ok(account.balance) } - pub async fn get_nonce(&mut self, address: &Address) -> Result { + pub async fn get_nonce(&self, address: &Address) -> Result { let payload = self.consensus.get_execution_payload().await?; let account = self.execution.get_account(&address, None, &payload).await?; Ok(account.nonce) } - pub async fn get_code(&mut self, address: &Address) -> Result> { + pub async fn get_code(&self, address: &Address) -> Result> { let payload = self.consensus.get_execution_payload().await?; self.execution.get_code(&address, &payload).await } - pub async fn get_storage_at(&mut self, address: &Address, slot: U256) -> Result { + pub async fn get_storage_at(&self, address: &Address, slot: U256) -> Result { let payload = self.consensus.get_execution_payload().await?; let account = self .execution diff --git a/src/client/mod.rs b/src/client/mod.rs index 4f72e22..e84ac30 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -1,2 +1,4 @@ mod client; pub use client::*; + +pub mod rpc; diff --git a/src/client/rpc.rs b/src/client/rpc.rs new file mode 100644 index 0000000..b1eda92 --- /dev/null +++ b/src/client/rpc.rs @@ -0,0 +1,128 @@ +use ethers::{abi::AbiEncode, types::{Address, U256}}; +use eyre::Result; +use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc}; +use serde::{Deserialize, Serialize}; + +use jsonrpsee::{ + core::{async_trait, Error}, + http_server::{HttpServerBuilder, HttpServerHandle}, + proc_macros::rpc, +}; + +use crate::common::utils::hex_str_to_bytes; + +use super::Client; + +pub struct Rpc { + client: Arc, + handle: Option, +} + +impl Rpc { + pub fn new(client: Arc) -> Self { + Rpc { + client, + handle: None, + } + } + + pub async fn start(&mut self) -> Result { + let rpc_inner = RpcInner { + client: self.client.clone(), + }; + let (handle, addr) = start(rpc_inner).await?; + self.handle = Some(handle); + Ok(addr) + } +} + +#[rpc(client, server, namespace = "eth")] +trait EthRpc { + #[method(name = "getBalance")] + async fn get_balance(&self, address: &str, block: &str) -> Result; + #[method(name = "getTransactionCount")] + async fn get_transaction_count(&self, address: &str, block: &str) -> Result; + #[method(name = "getCode")] + async fn get_code(&self, address: &str, block: &str) -> Result; + #[method(name = "call")] + async fn call(&self, opts: CallOpts, block: &str) -> Result; +} + +struct RpcInner { + pub client: Arc, +} + +#[async_trait] +impl EthRpcServer for RpcInner { + async fn get_balance(&self, address: &str, block: &str) -> Result { + match block { + "latest" => { + let address = convert_err(Address::from_str(address))?; + let balance = convert_err(self.client.get_balance(&address).await)?; + + Ok(balance.encode_hex()) + } + _ => Err(Error::Custom("Invalid Block Number".to_string())), + } + } + + async fn get_transaction_count(&self, address: &str, block: &str) -> Result { + match block { + "latest" => { + let address = convert_err(Address::from_str(address))?; + let nonce = convert_err(self.client.get_nonce(&address).await)?; + + Ok(nonce.encode_hex()) + } + _ => Err(Error::Custom("Invalid Block Number".to_string())), + } + } + + async fn get_code(&self, address: &str, block: &str) -> Result { + match block { + "latest" => { + let address = convert_err(Address::from_str(address))?; + let code = convert_err(self.client.get_code(&address).await)?; + + Ok(hex::encode(code)) + } + _ => Err(Error::Custom("Invalid Block Number".to_string())), + } + } + + async fn call(&self, opts: CallOpts, block: &str) -> Result { + match block { + "latest" => { + let to = convert_err(Address::from_str(&opts.to))?; + let data = convert_err(hex_str_to_bytes(&opts.data.unwrap_or("0x".to_string())))?; + let value = convert_err(U256::from_str_radix(&opts.value.unwrap_or("0x0".to_string()), 16))?; + + let res = convert_err(self.client.call(&to, &data, value).await)?; + Ok(hex::encode(res)) + }, + _ => Err(Error::Custom("Invalid Block Number".to_string())), + } + } +} + +async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> { + let server = HttpServerBuilder::default().build("127.0.0.1:8545").await?; + + let addr = server.local_addr()?; + let handle = server.start(rpc.into_rpc())?; + + Ok((handle, addr)) +} + +fn convert_err(res: Result) -> Result { + res.map_err(|err| Error::Custom(err.to_string())) +} + +#[derive(Deserialize, Serialize)] +pub struct CallOpts { + from: Option, + to: String, + gas: Option, + value: Option, + data: Option, +} diff --git a/src/consensus/consensus.rs b/src/consensus/consensus.rs index d4ee6d5..3466bc5 100644 --- a/src/consensus/consensus.rs +++ b/src/consensus/consensus.rs @@ -47,11 +47,11 @@ impl ConsensusClient { Ok(ConsensusClient { rpc, store }) } - pub async fn get_execution_payload(&mut self) -> Result { + pub async fn get_execution_payload(&self) -> Result { let slot = self.store.header.slot; - let mut block = self.rpc.get_block(slot).await?; + let mut block = self.rpc.get_block(slot).await?.clone(); let block_hash = block.hash_tree_root()?; - let verified_block_hash = self.store.header.hash_tree_root()?; + let verified_block_hash = self.store.header.clone().hash_tree_root()?; if verified_block_hash != block_hash { Err(eyre::eyre!("Block Root Mismatch")) diff --git a/src/consensus/types.rs b/src/consensus/types.rs index 3351224..450d906 100644 --- a/src/consensus/types.rs +++ b/src/consensus/types.rs @@ -11,7 +11,7 @@ pub type Address = Vector; pub type LogsBloom = Vector; pub type Transaction = List; -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] pub struct BeaconBlock { #[serde(deserialize_with = "u64_deserialize")] pub slot: u64, @@ -24,7 +24,7 @@ pub struct BeaconBlock { pub body: BeaconBlockBody, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] pub struct BeaconBlockBody { #[serde(deserialize_with = "signature_deserialize")] randao_reveal: SignatureBytes, @@ -44,7 +44,7 @@ pub struct BeaconBlockBody { pub execution_payload: ExecutionPayload, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] pub struct ExecutionPayload { #[serde(deserialize_with = "bytes32_deserialize")] parent_hash: Bytes32, @@ -76,7 +76,7 @@ pub struct ExecutionPayload { transactions: List, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] struct Attestation { aggregation_bits: Bitlist<2048>, data: AttestationData, @@ -84,7 +84,7 @@ struct Attestation { signature: SignatureBytes, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] struct AttestationData { #[serde(deserialize_with = "u64_deserialize")] slot: u64, @@ -96,7 +96,7 @@ struct AttestationData { target: Checkpoint, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] struct Checkpoint { #[serde(deserialize_with = "u64_deserialize")] epoch: u64, @@ -104,12 +104,12 @@ struct Checkpoint { root: Bytes32, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] struct Dummy { t: u64, } -#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] +#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)] pub struct Eth1Data { #[serde(deserialize_with = "bytes32_deserialize")] deposit_root: Bytes32, diff --git a/src/execution/evm.rs b/src/execution/evm.rs index 972e138..33c7add 100644 --- a/src/execution/evm.rs +++ b/src/execution/evm.rs @@ -1,20 +1,20 @@ -use std::str::FromStr; +use std::{str::FromStr, sync::Arc, thread}; use bytes::Bytes; +use ethers::prelude::{Address, H160, H256, U256}; use eyre::Result; -use ethers::prelude::{U256, H256, H160, Address}; -use revm::{Database, Bytecode, AccountInfo, EVM, Env, TransactTo, TransactOut}; -use futures::executor::block_on; +use revm::{AccountInfo, Bytecode, Database, Env, TransactOut, TransactTo, EVM}; +use tokio::runtime::Runtime; -use crate::consensus::types::ExecutionPayload; use super::ExecutionClient; +use crate::consensus::types::ExecutionPayload; pub struct Evm { - evm: EVM + evm: EVM, } impl Evm { - pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { + pub fn new(execution: Arc, payload: ExecutionPayload) -> Self { let mut evm: EVM = EVM::new(); let db = ProofDB::new(execution, payload); evm.database(db); @@ -23,7 +23,6 @@ impl Evm { } pub fn call(&mut self, to: &Address, calldata: &Vec, value: U256) -> Result> { - let mut env = Env::default(); let mut tx = revm::TxEnv::default(); tx.transact_to = TransactTo::Call(*to); @@ -48,13 +47,13 @@ impl Evm { } struct ProofDB { - execution: ExecutionClient, + execution: Arc, payload: ExecutionPayload, error: Option, } impl ProofDB { - pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { + pub fn new(execution: Arc, payload: ExecutionPayload) -> Self { ProofDB { execution, payload, @@ -68,7 +67,7 @@ impl ProofDB { Err(err) => { self.error = Some(err.to_string()); T::default() - }, + } } } } @@ -77,14 +76,32 @@ impl Database for ProofDB { fn basic(&mut self, address: H160) -> AccountInfo { if is_precompile(&address) { - return AccountInfo::default() + return AccountInfo::default(); } - let account_future = self.execution.get_account(&address, None, &self.payload); - let account = self.safe_unwrap(block_on(account_future)); + let execution = self.execution.clone(); + let addr = address.clone(); + let payload = self.payload.clone(); - let bytecode_future = self.execution.get_code(&address, &self.payload); - let bytecode = self.safe_unwrap(block_on(bytecode_future)); + let handle = thread::spawn(move || { + let account_fut = execution.get_account(&addr, None, &payload); + let runtime = Runtime::new()?; + runtime.block_on(account_fut) + }); + + let account = self.safe_unwrap(handle.join().unwrap()); + + let execution = self.execution.clone(); + let addr = address.clone(); + let payload = self.payload.clone(); + + let handle = thread::spawn(move || { + let code_fut = execution.get_code(&addr, &payload); + let runtime = Runtime::new()?; + runtime.block_on(code_fut) + }); + + let bytecode = self.safe_unwrap(handle.join().unwrap()); let bytecode = Bytecode::new_raw(Bytes::from(bytecode)); AccountInfo::new(account.balance, account.nonce.as_u64(), bytecode) @@ -95,10 +112,19 @@ impl Database for ProofDB { } fn storage(&mut self, address: H160, slot: U256) -> U256 { - let slots = [slot]; - let account_future = self.execution.get_account(&address, Some(&slots), &self.payload); - let account = self.safe_unwrap(block_on(account_future)); + let execution = self.execution.clone(); + let addr = address.clone(); + let slots = [slot]; + let payload = self.payload.clone(); + + let handle = thread::spawn(move || { + let account_fut = execution.get_account(&addr, Some(&slots), &payload); + let runtime = Runtime::new()?; + runtime.block_on(account_fut) + }); + + let account = self.safe_unwrap(handle.join().unwrap()); *account.slots.get(&slot).unwrap() } diff --git a/src/execution/execution.rs b/src/execution/execution.rs index 92158e4..7209c2b 100644 --- a/src/execution/execution.rs +++ b/src/execution/execution.rs @@ -30,6 +30,7 @@ impl ExecutionClient { payload: &ExecutionPayload, ) -> Result { let slots = slots.unwrap_or(&[]); + let proof = self .rpc .get_proof(&address, slots, payload.block_number) diff --git a/src/execution/mod.rs b/src/execution/mod.rs index e451d20..190e460 100644 --- a/src/execution/mod.rs +++ b/src/execution/mod.rs @@ -1,5 +1,5 @@ -pub mod types; pub mod evm; +pub mod types; mod execution; pub use execution::*; diff --git a/src/execution/types.rs b/src/execution/types.rs index dfa716d..988bbb3 100644 --- a/src/execution/types.rs +++ b/src/execution/types.rs @@ -29,7 +29,7 @@ pub struct StorageProof { pub proof: Vec>, } -#[derive(Default)] +#[derive(Default, Debug)] pub struct Account { pub balance: U256, pub nonce: U256, diff --git a/src/main.rs b/src/main.rs index 094bc70..6b98b28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,9 @@ -use std::str::FromStr; +use std::{sync::Arc, time::Duration}; -use ethers::prelude::{Address, U256}; use eyre::Result; -use client::Client; - -use crate::common::utils::hex_str_to_bytes; +use client::{rpc::Rpc, Client}; +use tokio::time::sleep; pub mod client; pub mod common; @@ -21,24 +19,11 @@ async fn main() -> Result<()> { let mut client = Client::new(consensus_rpc, execution_rpc, checkpoint).await?; client.sync().await?; - let header = client.get_header(); - println!("synced up to slot: {}", header.slot); + let mut rpc = Rpc::new(Arc::new(client)); + let addr = rpc.start().await?; + println!("{}", addr); - 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?; - let storage_value = client.get_storage_at(&address, U256::from(0)).await?; - - let owner_calldata = hex_str_to_bytes("0x8da5cb5b")?; - let value = U256::from(0); - let owner = client.call(&address, &owner_calldata, value).await?; - - println!("balance: {}", balance); - println!("nonce: {}", nonce); - println!("code: 0x{}...", hex::encode(code[..5].to_vec())); - println!("value at slot 0: 0x{:x}", storage_value); - println!("result of calling owner() on address: {}", hex::encode(owner)); + sleep(Duration::from_secs(300)).await; Ok(()) }