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 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<ExecutionClient>,
}
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<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 mut evm = Evm::new(self.execution.clone(), payload);
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 account = self.execution.get_account(&address, None, &payload).await?;
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 account = self.execution.get_account(&address, None, &payload).await?;
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?;
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 account = self
.execution

View File

@ -1,2 +1,4 @@
mod 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 })
}
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 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"))

View File

@ -11,7 +11,7 @@ pub type Address = Vector<u8, 20>;
pub type LogsBloom = Vector<u8, 256>;
pub type Transaction = List<u8, 1073741824>;
#[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<Transaction, 1048576>,
}
#[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,

View File

@ -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<ProofDB>
evm: EVM<ProofDB>,
}
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 db = ProofDB::new(execution, payload);
evm.database(db);
@ -23,7 +23,6 @@ impl Evm {
}
pub fn call(&mut self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> {
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<ExecutionClient>,
payload: ExecutionPayload,
error: Option<String>,
}
impl ProofDB {
pub fn new(execution: ExecutionClient, payload: ExecutionPayload) -> Self {
pub fn new(execution: Arc<ExecutionClient>, 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()
}

View File

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

View File

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

View File

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