diff --git a/client/src/client.rs b/client/src/client.rs index 3620fb1..b4aed2b 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -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, + payloads: HashMap, + 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, value: U256) -> Result> { - let payload = self.consensus.get_execution_payload().await?; + pub fn call( + &self, + to: &Address, + calldata: &Vec, + value: U256, + block: &Option, + ) -> Result> { + 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, value: U256) -> Result { - let payload = self.consensus.get_execution_payload().await?; + pub fn estimate_gas(&self, to: &Address, calldata: &Vec, value: U256) -> Result { + 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 { - let payload = self.consensus.get_execution_payload().await?; + pub async fn get_balance(&self, address: &Address, block: &Option) -> Result { + 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 { - let payload = self.consensus.get_execution_payload().await?; + pub async fn get_nonce(&self, address: &Address, block: &Option) -> Result { + 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> { - let payload = self.consensus.get_execution_payload().await?; + pub async fn get_code(&self, address: &Address, block: &Option) -> Result> { + 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 { - 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 { - let payload = self.consensus.get_execution_payload().await?; + pub fn get_gas_price(&self) -> Result { + 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 { + pub fn get_priority_fee(&self) -> Result { let tip = U256::from(10_u64.pow(9)); Ok(tip) } - pub async fn get_block_number(&self) -> Result { - let payload = self.consensus.get_execution_payload().await?; + pub fn get_block_number(&self) -> Result { + let payload = self.get_payload(&None)?; Ok(payload.block_number) } + pub fn get_block_by_number(&self, block: &Option) -> Result { + 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) -> Result { + 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")), + } + } + } + } } diff --git a/client/src/rpc.rs b/client/src/rpc.rs index 7790338..972c388 100644 --- a/client/src/rpc.rs +++ b/client/src/rpc.rs @@ -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>, @@ -63,8 +63,17 @@ trait EthRpc { async fn max_priority_fee_per_gas(&self) -> Result; #[method(name = "blockNumber")] async fn block_number(&self) -> Result; + #[method(name = "getBlockByNumber")] + async fn get_block_by_number(&self, num: &str, full_tx: bool) -> Result; } +#[rpc(client, server, namespace = "net")] +trait NetRpc { + #[method(name = "version")] + async fn version(&self) -> Result; +} + +#[derive(Clone)] struct RpcInner { client: Arc>, port: u16, @@ -73,60 +82,45 @@ struct RpcInner { #[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 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 { - 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 { - 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 { - 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 { @@ -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 { 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 { 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 { 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 { + 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 { + 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(res: Result) -> Result { - 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> { + 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)] diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 67b60be..a8c04e9 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -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 { - let slot = self.store.optimistic_header.slot; + pub async fn get_execution_payload(&self, slot: &Option) -> Result { + 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 diff --git a/consensus/src/types.rs b/consensus/src/types.rs index 8f46cb3..d39f4fa 100644 --- a/consensus/src/types.rs +++ b/consensus/src/types.rs @@ -36,8 +36,7 @@ pub struct BeaconBlockBody { // TODO: handle attester_slashings: List, attestations: List, - // TODO: handle - deposits: List, + deposits: List, // TODO: handle voluntary_exits: List, 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, + pub logs_bloom: Vector, #[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, + pub extra_data: List, #[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, + pub transactions: List, } #[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, + 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, D::Error> +where + D: serde::Deserializer<'de>, +{ + let elems: Vec = serde::Deserialize::deserialize(deserializer)?; + Ok(elems + .iter() + .map(|elem| { + let elem_bytes = hex_str_to_bytes(elem)?; + Ok(Vector::from_iter(elem_bytes)) + }) + .collect::>>() + .map_err(D::Error::custom)?) +} + fn signature_deserialize<'de, D>(deserializer: D) -> Result where D: serde::Deserializer<'de>, diff --git a/execution/src/execution.rs b/execution/src/execution.rs index 6baffb9..a083fac 100644 --- a/execution/src/execution.rs +++ b/execution/src/execution.rs @@ -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 { + 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::>(); + + 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![], + }) + } } diff --git a/execution/src/types.rs b/execution/src/types.rs index 8f85c40..253cf8c 100644 --- a/execution/src/types.rs +++ b/execution/src/types.rs @@ -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, } +#[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, + #[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, + 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, + pub transactions_root: H256, + pub uncles: Vec, +} + +fn serialize_bytes(bytes: &Vec, s: S) -> Result +where + S: serde::Serializer, +{ + let bytes_str = format!("0x{}", hex::encode(bytes)); + s.serialize_str(&bytes_str) +} + +fn serialize_u64_string(x: &u64, s: S) -> Result +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>, D::Error> where D: serde::Deserializer<'de>,