expose basic rpc api

This commit is contained in:
Noah Citron 2022-08-25 21:18:47 -04:00
parent 072142b88f
commit e18aa704e1
10 changed files with 206 additions and 62 deletions

View File

@ -1,14 +1,16 @@
use std::sync::Arc;
use ethers::prelude::{Address, U256}; use ethers::prelude::{Address, U256};
use eyre::Result; use eyre::Result;
use crate::consensus::types::Header; use crate::consensus::types::Header;
use crate::consensus::ConsensusClient; use crate::consensus::ConsensusClient;
use crate::execution::ExecutionClient;
use crate::execution::evm::Evm; use crate::execution::evm::Evm;
use crate::execution::ExecutionClient;
pub struct Client { pub struct Client {
consensus: ConsensusClient, consensus: ConsensusClient,
execution: ExecutionClient, execution: Arc<ExecutionClient>,
} }
impl Client { impl Client {
@ -22,7 +24,7 @@ impl Client {
Ok(Client { Ok(Client {
consensus, consensus,
execution, execution: Arc::new(execution),
}) })
} }
@ -30,30 +32,30 @@ impl Client {
self.consensus.sync().await self.consensus.sync().await
} }
pub async fn call(&mut self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> { pub async fn call(&self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> {
let payload = self.consensus.get_execution_payload().await?; let payload = self.consensus.get_execution_payload().await?;
let mut evm = Evm::new(self.execution.clone(), payload); let mut evm = Evm::new(self.execution.clone(), payload);
evm.call(to, calldata, value) evm.call(to, calldata, value)
} }
pub async fn get_balance(&mut self, address: &Address) -> Result<U256> { pub async fn get_balance(&self, address: &Address) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?; let payload = self.consensus.get_execution_payload().await?;
let account = self.execution.get_account(&address, None, &payload).await?; let account = self.execution.get_account(&address, None, &payload).await?;
Ok(account.balance) Ok(account.balance)
} }
pub async fn get_nonce(&mut self, address: &Address) -> Result<U256> { pub async fn get_nonce(&self, address: &Address) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?; let payload = self.consensus.get_execution_payload().await?;
let account = self.execution.get_account(&address, None, &payload).await?; let account = self.execution.get_account(&address, None, &payload).await?;
Ok(account.nonce) Ok(account.nonce)
} }
pub async fn get_code(&mut self, address: &Address) -> Result<Vec<u8>> { pub async fn get_code(&self, address: &Address) -> Result<Vec<u8>> {
let payload = self.consensus.get_execution_payload().await?; let payload = self.consensus.get_execution_payload().await?;
self.execution.get_code(&address, &payload).await self.execution.get_code(&address, &payload).await
} }
pub async fn get_storage_at(&mut self, address: &Address, slot: U256) -> Result<U256> { pub async fn get_storage_at(&self, address: &Address, slot: U256) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?; let payload = self.consensus.get_execution_payload().await?;
let account = self let account = self
.execution .execution

View File

@ -1,2 +1,4 @@
mod client; mod client;
pub use client::*; pub use client::*;
pub mod rpc;

128
src/client/rpc.rs Normal file
View File

@ -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<Client>,
handle: Option<HttpServerHandle>,
}
impl Rpc {
pub fn new(client: Arc<Client>) -> Self {
Rpc {
client,
handle: None,
}
}
pub async fn start(&mut self) -> Result<SocketAddr> {
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<String, Error>;
#[method(name = "getTransactionCount")]
async fn get_transaction_count(&self, address: &str, block: &str) -> Result<String, Error>;
#[method(name = "getCode")]
async fn get_code(&self, address: &str, block: &str) -> Result<String, Error>;
#[method(name = "call")]
async fn call(&self, opts: CallOpts, block: &str) -> Result<String, Error>;
}
struct RpcInner {
pub client: Arc<Client>,
}
#[async_trait]
impl EthRpcServer for RpcInner {
async fn get_balance(&self, address: &str, block: &str) -> Result<String, Error> {
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<String, Error> {
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<String, Error> {
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<String, Error> {
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<T, E: Display>(res: Result<T, E>) -> Result<T, Error> {
res.map_err(|err| Error::Custom(err.to_string()))
}
#[derive(Deserialize, Serialize)]
pub struct CallOpts {
from: Option<String>,
to: String,
gas: Option<String>,
value: Option<String>,
data: Option<String>,
}

View File

@ -47,11 +47,11 @@ impl ConsensusClient {
Ok(ConsensusClient { rpc, store }) Ok(ConsensusClient { rpc, store })
} }
pub async fn get_execution_payload(&mut self) -> Result<ExecutionPayload> { pub async fn get_execution_payload(&self) -> Result<ExecutionPayload> {
let slot = self.store.header.slot; 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 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 { if verified_block_hash != block_hash {
Err(eyre::eyre!("Block Root Mismatch")) Err(eyre::eyre!("Block Root Mismatch"))

View File

@ -11,7 +11,7 @@ pub type Address = Vector<u8, 20>;
pub type LogsBloom = Vector<u8, 256>; pub type LogsBloom = Vector<u8, 256>;
pub type Transaction = List<u8, 1073741824>; pub type Transaction = List<u8, 1073741824>;
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct BeaconBlock { pub struct BeaconBlock {
#[serde(deserialize_with = "u64_deserialize")] #[serde(deserialize_with = "u64_deserialize")]
pub slot: u64, pub slot: u64,
@ -24,7 +24,7 @@ pub struct BeaconBlock {
pub body: BeaconBlockBody, pub body: BeaconBlockBody,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct BeaconBlockBody { pub struct BeaconBlockBody {
#[serde(deserialize_with = "signature_deserialize")] #[serde(deserialize_with = "signature_deserialize")]
randao_reveal: SignatureBytes, randao_reveal: SignatureBytes,
@ -44,7 +44,7 @@ pub struct BeaconBlockBody {
pub execution_payload: ExecutionPayload, pub execution_payload: ExecutionPayload,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct ExecutionPayload { pub struct ExecutionPayload {
#[serde(deserialize_with = "bytes32_deserialize")] #[serde(deserialize_with = "bytes32_deserialize")]
parent_hash: Bytes32, parent_hash: Bytes32,
@ -76,7 +76,7 @@ pub struct ExecutionPayload {
transactions: List<Transaction, 1048576>, transactions: List<Transaction, 1048576>,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Attestation { struct Attestation {
aggregation_bits: Bitlist<2048>, aggregation_bits: Bitlist<2048>,
data: AttestationData, data: AttestationData,
@ -84,7 +84,7 @@ struct Attestation {
signature: SignatureBytes, signature: SignatureBytes,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct AttestationData { struct AttestationData {
#[serde(deserialize_with = "u64_deserialize")] #[serde(deserialize_with = "u64_deserialize")]
slot: u64, slot: u64,
@ -96,7 +96,7 @@ struct AttestationData {
target: Checkpoint, target: Checkpoint,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Checkpoint { struct Checkpoint {
#[serde(deserialize_with = "u64_deserialize")] #[serde(deserialize_with = "u64_deserialize")]
epoch: u64, epoch: u64,
@ -104,12 +104,12 @@ struct Checkpoint {
root: Bytes32, root: Bytes32,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Dummy { struct Dummy {
t: u64, t: u64,
} }
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize)] #[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct Eth1Data { pub struct Eth1Data {
#[serde(deserialize_with = "bytes32_deserialize")] #[serde(deserialize_with = "bytes32_deserialize")]
deposit_root: Bytes32, deposit_root: Bytes32,

View File

@ -1,20 +1,20 @@
use std::str::FromStr; use std::{str::FromStr, sync::Arc, thread};
use bytes::Bytes; use bytes::Bytes;
use ethers::prelude::{Address, H160, H256, U256};
use eyre::Result; use eyre::Result;
use ethers::prelude::{U256, H256, H160, Address}; use revm::{AccountInfo, Bytecode, Database, Env, TransactOut, TransactTo, EVM};
use revm::{Database, Bytecode, AccountInfo, EVM, Env, TransactTo, TransactOut}; use tokio::runtime::Runtime;
use futures::executor::block_on;
use crate::consensus::types::ExecutionPayload;
use super::ExecutionClient; use super::ExecutionClient;
use crate::consensus::types::ExecutionPayload;
pub struct Evm { pub struct Evm {
evm: EVM<ProofDB> evm: EVM<ProofDB>,
} }
impl Evm { impl Evm {
pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { pub fn new(execution: Arc<ExecutionClient>, payload: ExecutionPayload) -> Self {
let mut evm: EVM<ProofDB> = EVM::new(); let mut evm: EVM<ProofDB> = EVM::new();
let db = ProofDB::new(execution, payload); let db = ProofDB::new(execution, payload);
evm.database(db); evm.database(db);
@ -23,7 +23,6 @@ impl Evm {
} }
pub fn call(&mut self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> { pub fn call(&mut self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> {
let mut env = Env::default(); let mut env = Env::default();
let mut tx = revm::TxEnv::default(); let mut tx = revm::TxEnv::default();
tx.transact_to = TransactTo::Call(*to); tx.transact_to = TransactTo::Call(*to);
@ -48,13 +47,13 @@ impl Evm {
} }
struct ProofDB { struct ProofDB {
execution: ExecutionClient, execution: Arc<ExecutionClient>,
payload: ExecutionPayload, payload: ExecutionPayload,
error: Option<String>, error: Option<String>,
} }
impl ProofDB { impl ProofDB {
pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self { pub fn new(execution: Arc<ExecutionClient>, payload: ExecutionPayload) -> Self {
ProofDB { ProofDB {
execution, execution,
payload, payload,
@ -68,7 +67,7 @@ impl ProofDB {
Err(err) => { Err(err) => {
self.error = Some(err.to_string()); self.error = Some(err.to_string());
T::default() T::default()
}, }
} }
} }
} }
@ -77,14 +76,32 @@ impl Database for ProofDB {
fn basic(&mut self, address: H160) -> AccountInfo { fn basic(&mut self, address: H160) -> AccountInfo {
if is_precompile(&address) { if is_precompile(&address) {
return AccountInfo::default() return AccountInfo::default();
} }
let account_future = self.execution.get_account(&address, None, &self.payload); let execution = self.execution.clone();
let account = self.safe_unwrap(block_on(account_future)); let addr = address.clone();
let payload = self.payload.clone();
let bytecode_future = self.execution.get_code(&address, &self.payload); let handle = thread::spawn(move || {
let bytecode = self.safe_unwrap(block_on(bytecode_future)); 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)); let bytecode = Bytecode::new_raw(Bytes::from(bytecode));
AccountInfo::new(account.balance, account.nonce.as_u64(), 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 { 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() *account.slots.get(&slot).unwrap()
} }

View File

@ -30,6 +30,7 @@ impl ExecutionClient {
payload: &ExecutionPayload, payload: &ExecutionPayload,
) -> Result<Account> { ) -> Result<Account> {
let slots = slots.unwrap_or(&[]); let slots = slots.unwrap_or(&[]);
let proof = self let proof = self
.rpc .rpc
.get_proof(&address, slots, payload.block_number) .get_proof(&address, slots, payload.block_number)

View File

@ -1,5 +1,5 @@
pub mod types;
pub mod evm; pub mod evm;
pub mod types;
mod execution; mod execution;
pub use execution::*; pub use execution::*;

View File

@ -29,7 +29,7 @@ pub struct StorageProof {
pub proof: Vec<Vec<u8>>, pub proof: Vec<Vec<u8>>,
} }
#[derive(Default)] #[derive(Default, Debug)]
pub struct Account { pub struct Account {
pub balance: U256, pub balance: U256,
pub nonce: U256, pub nonce: U256,

View File

@ -1,11 +1,9 @@
use std::str::FromStr; use std::{sync::Arc, time::Duration};
use ethers::prelude::{Address, U256};
use eyre::Result; use eyre::Result;
use client::Client; use client::{rpc::Rpc, Client};
use tokio::time::sleep;
use crate::common::utils::hex_str_to_bytes;
pub mod client; pub mod client;
pub mod common; pub mod common;
@ -21,24 +19,11 @@ async fn main() -> Result<()> {
let mut client = Client::new(consensus_rpc, execution_rpc, checkpoint).await?; let mut client = Client::new(consensus_rpc, execution_rpc, checkpoint).await?;
client.sync().await?; client.sync().await?;
let header = client.get_header(); let mut rpc = Rpc::new(Arc::new(client));
println!("synced up to slot: {}", header.slot); let addr = rpc.start().await?;
println!("{}", addr);
let address = Address::from_str("0x14f9D4aF749609c1438528C0Cce1cC3f6D411c47")?; sleep(Duration::from_secs(300)).await;
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));
Ok(()) Ok(())
} }