expose basic rpc api
This commit is contained in:
parent
072142b88f
commit
e18aa704e1
|
@ -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
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
mod client;
|
||||
pub use client::*;
|
||||
|
||||
pub mod rpc;
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -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"))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
pub mod types;
|
||||
pub mod evm;
|
||||
pub mod types;
|
||||
|
||||
mod execution;
|
||||
pub use execution::*;
|
||||
|
|
|
@ -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,
|
||||
|
|
29
src/main.rs
29
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue