feat: add metamask support (#2)

* add eth_getBlockByNumber and net_version

* fmt
This commit is contained in:
Noah Citron 2022-08-31 17:40:44 -04:00 committed by GitHub
parent 374dd1f38f
commit 92e30c3d50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 306 additions and 86 deletions

View File

@ -1,18 +1,22 @@
use std::collections::HashMap;
use std::sync::Arc;
use ethers::prelude::{Address, U256};
use eyre::Result;
use config::Config;
use consensus::types::Header;
use consensus::types::{ExecutionPayload, Header};
use consensus::ConsensusClient;
use execution::evm::Evm;
use execution::types::ExecutionBlock;
use execution::ExecutionClient;
pub struct Client {
consensus: ConsensusClient,
execution: ExecutionClient,
config: Arc<Config>,
payloads: HashMap<u64, ExecutionPayload>,
block_head: u64,
}
impl Client {
@ -25,52 +29,82 @@ impl Client {
ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone()).await?;
let execution = ExecutionClient::new(execution_rpc);
let payloads = HashMap::new();
Ok(Client {
consensus,
execution,
config,
payloads,
block_head: 0,
})
}
pub async fn sync(&mut self) -> Result<()> {
self.consensus.sync().await
self.consensus.sync().await?;
let head = self.consensus.get_head();
let payload = self
.consensus
.get_execution_payload(&Some(head.slot))
.await?;
self.block_head = payload.block_number;
self.payloads.insert(payload.block_number, payload);
Ok(())
}
pub async fn advance(&mut self) -> Result<()> {
self.consensus.advance().await
self.consensus.advance().await?;
let head = self.consensus.get_head();
let payload = self
.consensus
.get_execution_payload(&Some(head.slot))
.await?;
self.block_head = payload.block_number;
self.payloads.insert(payload.block_number, payload);
Ok(())
}
pub async fn call(&self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<Vec<u8>> {
let payload = self.consensus.get_execution_payload().await?;
pub fn call(
&self,
to: &Address,
calldata: &Vec<u8>,
value: U256,
block: &Option<u64>,
) -> Result<Vec<u8>> {
let payload = self.get_payload(block)?;
let mut evm = Evm::new(self.execution.clone(), payload);
evm.call(to, calldata, value)
}
pub async fn estimate_gas(&self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<u64> {
let payload = self.consensus.get_execution_payload().await?;
pub fn estimate_gas(&self, to: &Address, calldata: &Vec<u8>, value: U256) -> Result<u64> {
let payload = self.get_payload(&None)?;
let mut evm = Evm::new(self.execution.clone(), payload);
evm.estimate_gas(to, calldata, value)
}
pub async fn get_balance(&self, address: &Address) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?;
pub async fn get_balance(&self, address: &Address, block: &Option<u64>) -> Result<U256> {
let payload = self.get_payload(block)?;
let account = self.execution.get_account(&address, None, &payload).await?;
Ok(account.balance)
}
pub async fn get_nonce(&self, address: &Address) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?;
pub async fn get_nonce(&self, address: &Address, block: &Option<u64>) -> Result<U256> {
let payload = self.get_payload(block)?;
let account = self.execution.get_account(&address, None, &payload).await?;
Ok(account.nonce)
}
pub async fn get_code(&self, address: &Address) -> Result<Vec<u8>> {
let payload = self.consensus.get_execution_payload().await?;
pub async fn get_code(&self, address: &Address, block: &Option<u64>) -> Result<Vec<u8>> {
let payload = self.get_payload(block)?;
self.execution.get_code(&address, &payload).await
}
pub async fn get_storage_at(&self, address: &Address, slot: U256) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?;
let payload = self.get_payload(&None)?;
let account = self
.execution
.get_account(address, Some(&[slot]), &payload)
@ -82,23 +116,28 @@ impl Client {
}
}
pub async fn get_gas_price(&self) -> Result<U256> {
let payload = self.consensus.get_execution_payload().await?;
pub fn get_gas_price(&self) -> Result<U256> {
let payload = self.get_payload(&None)?;
let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le());
let tip = U256::from(10_u64.pow(9));
Ok(base_fee + tip)
}
pub async fn get_priority_fee(&self) -> Result<U256> {
pub fn get_priority_fee(&self) -> Result<U256> {
let tip = U256::from(10_u64.pow(9));
Ok(tip)
}
pub async fn get_block_number(&self) -> Result<u64> {
let payload = self.consensus.get_execution_payload().await?;
pub fn get_block_number(&self) -> Result<u64> {
let payload = self.get_payload(&None)?;
Ok(payload.block_number)
}
pub fn get_block_by_number(&self, block: &Option<u64>) -> Result<ExecutionBlock> {
let payload = self.get_payload(block)?;
self.execution.get_block(&payload)
}
pub fn chain_id(&self) -> u64 {
self.config.general.chain_id
}
@ -106,4 +145,23 @@ impl Client {
pub fn get_header(&self) -> &Header {
self.consensus.get_head()
}
fn get_payload(&self, block: &Option<u64>) -> Result<ExecutionPayload> {
match block {
Some(block) => {
let payload = self.payloads.get(block);
match payload {
Some(payload) => Ok(payload.clone()),
None => Err(eyre::eyre!("Block Not Found")),
}
}
None => {
let payload = self.payloads.get(&self.block_head);
match payload {
Some(payload) => Ok(payload.clone()),
None => Err(eyre::eyre!("Block Not Found")),
}
}
}
}
}

View File

@ -8,14 +8,14 @@ use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
use tokio::sync::Mutex;
use jsonrpsee::{
core::{async_trait, Error},
core::{async_trait, server::rpc_module::Methods, Error},
http_server::{HttpServerBuilder, HttpServerHandle},
proc_macros::rpc,
};
use common::utils::{hex_str_to_bytes, u64_to_hex_string};
use super::Client;
use common::utils::{hex_str_to_bytes, u64_to_hex_string};
use execution::types::ExecutionBlock;
pub struct Rpc {
client: Arc<Mutex<Client>>,
@ -63,8 +63,17 @@ trait EthRpc {
async fn max_priority_fee_per_gas(&self) -> Result<String, Error>;
#[method(name = "blockNumber")]
async fn block_number(&self) -> Result<String, Error>;
#[method(name = "getBlockByNumber")]
async fn get_block_by_number(&self, num: &str, full_tx: bool) -> Result<ExecutionBlock, Error>;
}
#[rpc(client, server, namespace = "net")]
trait NetRpc {
#[method(name = "version")]
async fn version(&self) -> Result<String, Error>;
}
#[derive(Clone)]
struct RpcInner {
client: Arc<Mutex<Client>>,
port: u16,
@ -73,60 +82,45 @@ struct RpcInner {
#[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 client = self.client.lock().await;
let balance = convert_err(client.get_balance(&address).await)?;
let block = convert_err(decode_block(block))?;
let address = convert_err(Address::from_str(address))?;
let client = self.client.lock().await;
let balance = convert_err(client.get_balance(&address, &block).await)?;
Ok(balance.encode_hex())
}
_ => Err(Error::Custom("Invalid Block Number".to_string())),
}
Ok(balance.encode_hex())
}
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 client = self.client.lock().await;
let nonce = convert_err(client.get_nonce(&address).await)?;
let block = convert_err(decode_block(block))?;
let address = convert_err(Address::from_str(address))?;
let client = self.client.lock().await;
let nonce = convert_err(client.get_nonce(&address, &block).await)?;
Ok(nonce.encode_hex())
}
_ => Err(Error::Custom("Invalid Block Number".to_string())),
}
Ok(nonce.encode_hex())
}
async fn get_code(&self, address: &str, block: &str) -> Result<String, Error> {
match block {
"latest" => {
let address = convert_err(Address::from_str(address))?;
let client = self.client.lock().await;
let code = convert_err(client.get_code(&address).await)?;
let block = convert_err(decode_block(block))?;
let address = convert_err(Address::from_str(address))?;
let client = self.client.lock().await;
let code = convert_err(client.get_code(&address, &block).await)?;
Ok(hex::encode(code))
}
_ => Err(Error::Custom("Invalid Block Number".to_string())),
}
Ok(hex::encode(code))
}
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 block = convert_err(decode_block(block))?;
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 client = self.client.lock().await;
let res = convert_err(client.call(&to, &data, value).await)?;
Ok(hex::encode(res))
}
_ => Err(Error::Custom("Invalid Block Number".to_string())),
}
let client = self.client.lock().await;
let res = convert_err(client.call(&to, &data, value, &block))?;
Ok(hex::encode(res))
}
async fn estimate_gas(&self, opts: CallOpts) -> Result<String, Error> {
@ -138,7 +132,7 @@ impl EthRpcServer for RpcInner {
))?;
let client = self.client.lock().await;
let gas = convert_err(client.estimate_gas(&to, &data, value).await)?;
let gas = convert_err(client.estimate_gas(&to, &data, value))?;
Ok(u64_to_hex_string(gas))
}
@ -150,21 +144,41 @@ impl EthRpcServer for RpcInner {
async fn gas_price(&self) -> Result<String, Error> {
let client = self.client.lock().await;
let gas_price = convert_err(client.get_gas_price().await)?;
let gas_price = convert_err(client.get_gas_price())?;
Ok(gas_price.encode_hex())
}
async fn max_priority_fee_per_gas(&self) -> Result<String, Error> {
let client = self.client.lock().await;
let tip = convert_err(client.get_priority_fee().await)?;
let tip = convert_err(client.get_priority_fee())?;
Ok(tip.encode_hex())
}
async fn block_number(&self) -> Result<String, Error> {
let client = self.client.lock().await;
let num = convert_err(client.get_block_number().await)?;
let num = convert_err(client.get_block_number())?;
Ok(u64_to_hex_string(num))
}
async fn get_block_by_number(
&self,
block: &str,
_full_tx: bool,
) -> Result<ExecutionBlock, Error> {
let block = convert_err(decode_block(block))?;
let client = self.client.lock().await;
let block = convert_err(client.get_block_by_number(&block))?;
Ok(block)
}
}
#[async_trait]
impl NetRpcServer for RpcInner {
async fn version(&self) -> Result<String, Error> {
let client = self.client.lock().await;
Ok(client.chain_id().to_string())
}
}
async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> {
@ -172,13 +186,40 @@ async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> {
let server = HttpServerBuilder::default().build(addr).await?;
let addr = server.local_addr()?;
let handle = server.start(rpc.into_rpc())?;
let mut methods = Methods::new();
let eth_methods: Methods = EthRpcServer::into_rpc(rpc.clone()).into();
let net_methods: Methods = NetRpcServer::into_rpc(rpc).into();
methods.merge(eth_methods)?;
methods.merge(net_methods)?;
let handle = server.start(methods)?;
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()))
res.map_err(|err| {
println!("{}", err.to_string());
Error::Custom(err.to_string())
})
}
fn decode_block(block: &str) -> Result<Option<u64>> {
match block {
"latest" => Ok(None),
_ => {
if block.starts_with("0x") {
Ok(Some(u64::from_str_radix(
block.strip_prefix("0x").unwrap(),
16,
)?))
} else {
Ok(Some(block.parse()?))
}
}
}
}
#[derive(Deserialize, Serialize)]

View File

@ -57,7 +57,7 @@ impl ConsensusClient {
finalized_header: bootstrap.header.clone(),
current_sync_committee: bootstrap.current_sync_committee,
next_sync_committee: None,
optimistic_header: bootstrap.header,
optimistic_header: bootstrap.header.clone(),
previous_max_active_participants: 0,
current_max_active_participants: 0,
};
@ -65,8 +65,8 @@ impl ConsensusClient {
Ok(ConsensusClient { rpc, store, config })
}
pub async fn get_execution_payload(&self) -> Result<ExecutionPayload> {
let slot = self.store.optimistic_header.slot;
pub async fn get_execution_payload(&self, slot: &Option<u64>) -> Result<ExecutionPayload> {
let slot = slot.unwrap_or(self.store.optimistic_header.slot);
let mut block = self.rpc.get_block(slot).await?.clone();
let block_hash = block.hash_tree_root()?;
let verified_block_hash = self.store.optimistic_header.clone().hash_tree_root()?;
@ -319,6 +319,7 @@ impl ConsensusClient {
&& update.attested_header.slot > self.store.optimistic_header.slot
{
self.store.optimistic_header = update.attested_header.clone();
println!(
"applying optimistic update for slot: {}",
self.store.optimistic_header.slot

View File

@ -36,8 +36,7 @@ pub struct BeaconBlockBody {
// TODO: handle
attester_slashings: List<Dummy, 2>,
attestations: List<Attestation, 128>,
// TODO: handle
deposits: List<Dummy, 16>,
deposits: List<Deposit, 16>,
// TODO: handle
voluntary_exits: List<Dummy, 16>,
sync_aggregate: SyncAggregate,
@ -47,33 +46,33 @@ pub struct BeaconBlockBody {
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct ExecutionPayload {
#[serde(deserialize_with = "bytes32_deserialize")]
parent_hash: Bytes32,
pub parent_hash: Bytes32,
#[serde(deserialize_with = "address_deserialize")]
fee_recipient: Address,
pub fee_recipient: Address,
#[serde(deserialize_with = "bytes32_deserialize")]
pub state_root: Bytes32,
#[serde(deserialize_with = "bytes32_deserialize")]
pub receipts_root: Bytes32,
#[serde(deserialize_with = "logs_bloom_deserialize")]
logs_bloom: Vector<u8, 256>,
pub logs_bloom: Vector<u8, 256>,
#[serde(deserialize_with = "bytes32_deserialize")]
prev_randao: Bytes32,
pub prev_randao: Bytes32,
#[serde(deserialize_with = "u64_deserialize")]
pub block_number: u64,
#[serde(deserialize_with = "u64_deserialize")]
gas_limit: u64,
pub gas_limit: u64,
#[serde(deserialize_with = "u64_deserialize")]
gas_used: u64,
pub gas_used: u64,
#[serde(deserialize_with = "u64_deserialize")]
timestamp: u64,
pub timestamp: u64,
#[serde(deserialize_with = "extra_data_deserialize")]
extra_data: List<u8, 32>,
pub extra_data: List<u8, 32>,
#[serde(deserialize_with = "u256_deserialize")]
pub base_fee_per_gas: U256,
#[serde(deserialize_with = "bytes32_deserialize")]
pub block_hash: Bytes32,
#[serde(deserialize_with = "transactions_deserialize")]
transactions: List<Transaction, 1048576>,
pub transactions: List<Transaction, 1048576>,
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
@ -109,6 +108,25 @@ struct Dummy {
t: u64,
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
struct Deposit {
#[serde(deserialize_with = "bytes_vector_deserialize")]
proof: Vector<Bytes32, 33>,
data: DepositData,
}
#[derive(serde::Deserialize, Default, Debug, SimpleSerialize, Clone)]
struct DepositData {
#[serde(deserialize_with = "pubkey_deserialize")]
pubkey: BLSPubKey,
#[serde(deserialize_with = "bytes32_deserialize")]
withdrawal_credentials: Bytes32,
#[serde(deserialize_with = "u64_deserialize")]
amount: u64,
#[serde(deserialize_with = "signature_deserialize")]
signature: SignatureBytes,
}
#[derive(serde::Deserialize, Debug, Default, SimpleSerialize, Clone)]
pub struct Eth1Data {
#[serde(deserialize_with = "bytes32_deserialize")]
@ -213,6 +231,21 @@ where
.map_err(D::Error::custom)?)
}
fn bytes_vector_deserialize<'de, D>(deserializer: D) -> Result<Vector<Bytes32, 33>, D::Error>
where
D: serde::Deserializer<'de>,
{
let elems: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
Ok(elems
.iter()
.map(|elem| {
let elem_bytes = hex_str_to_bytes(elem)?;
Ok(Vector::from_iter(elem_bytes))
})
.collect::<Result<Vector<Bytes32, 33>>>()
.map_err(D::Error::custom)?)
}
fn signature_deserialize<'de, D>(deserializer: D) -> Result<SignatureBytes, D::Error>
where
D: serde::Deserializer<'de>,

View File

@ -1,7 +1,9 @@
use std::collections::HashMap;
use std::str::FromStr;
use ethers::abi::AbiEncode;
use ethers::prelude::{Address, U256};
use ethers::types::H256;
use ethers::utils::keccak256;
use ethers::utils::rlp::encode;
use eyre::Result;
@ -11,7 +13,7 @@ use consensus::types::ExecutionPayload;
use super::proof::{encode_account, verify_proof};
use super::rpc::Rpc;
use super::types::Account;
use super::types::{Account, ExecutionBlock};
#[derive(Clone)]
pub struct ExecutionClient {
@ -94,4 +96,39 @@ impl ExecutionClient {
Ok(code)
}
pub fn get_block(&self, payload: &ExecutionPayload) -> Result<ExecutionBlock> {
let empty_nonce = "0x0000000000000000".to_string();
let empty_uncle_hash = "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347";
let txs = payload
.transactions
.iter()
.map(|tx| H256::from_slice(&keccak256(tx.to_vec())))
.collect::<Vec<H256>>();
Ok(ExecutionBlock {
number: payload.block_number,
base_fee_per_gas: U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()),
difficulty: U256::from(0),
extra_data: payload.extra_data.to_vec(),
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
hash: H256::from_slice(&payload.block_hash),
logs_bloom: payload.logs_bloom.to_vec(),
miner: Address::from_slice(&payload.fee_recipient),
parent_hash: H256::from_slice(&payload.parent_hash),
receipts_root: H256::from_slice(&payload.receipts_root),
state_root: H256::from_slice(&payload.state_root),
timestamp: payload.timestamp,
total_difficulty: 0,
transactions: txs,
mix_hash: H256::from_slice(&payload.prev_randao),
nonce: empty_nonce,
sha3_uncles: H256::from_str(empty_uncle_hash)?,
size: 0,
transactions_root: H256::default(),
uncles: vec![],
})
}
}

View File

@ -3,9 +3,9 @@ use std::collections::HashMap;
use ethers::prelude::{Address, H256, U256};
use eyre::Result;
use serde::de::Error;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use common::utils::hex_str_to_bytes;
use common::utils::{hex_str_to_bytes, u64_to_hex_string};
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
@ -38,6 +38,56 @@ pub struct Account {
pub slots: HashMap<U256, U256>,
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct ExecutionBlock {
#[serde(serialize_with = "serialize_u64_string")]
pub number: u64,
pub base_fee_per_gas: U256,
pub difficulty: U256,
#[serde(serialize_with = "serialize_bytes")]
pub extra_data: Vec<u8>,
#[serde(serialize_with = "serialize_u64_string")]
pub gas_limit: u64,
#[serde(serialize_with = "serialize_u64_string")]
pub gas_used: u64,
pub hash: H256,
#[serde(serialize_with = "serialize_bytes")]
pub logs_bloom: Vec<u8>,
pub miner: Address,
pub mix_hash: H256,
pub nonce: String,
pub parent_hash: H256,
pub receipts_root: H256,
pub sha3_uncles: H256,
#[serde(serialize_with = "serialize_u64_string")]
pub size: u64,
pub state_root: H256,
#[serde(serialize_with = "serialize_u64_string")]
pub timestamp: u64,
#[serde(serialize_with = "serialize_u64_string")]
pub total_difficulty: u64,
pub transactions: Vec<H256>,
pub transactions_root: H256,
pub uncles: Vec<H256>,
}
fn serialize_bytes<S>(bytes: &Vec<u8>, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let bytes_str = format!("0x{}", hex::encode(bytes));
s.serialize_str(&bytes_str)
}
fn serialize_u64_string<S>(x: &u64, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let num_string = u64_to_hex_string(*x);
s.serialize_str(&num_string)
}
fn proof_deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
where
D: serde::Deserializer<'de>,