diff --git a/Cargo.lock b/Cargo.lock index e9bfead..f48eefa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1609,6 +1609,7 @@ dependencies = [ "serde", "ssz-rs", "tokio", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d108745..0b6469b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,4 @@ jsonrpsee = { version = "0.15.1", features = ["full"] } revm = "1.9.0" bytes = "1.2.1" futures = "0.3.23" +toml = "0.5.9" diff --git a/configs/goerli.toml b/configs/goerli.toml new file mode 100644 index 0000000..ae7d8f9 --- /dev/null +++ b/configs/goerli.toml @@ -0,0 +1,20 @@ +[general] +chain_id = 5 +genesis_root = "0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb" +checkpoint = "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99" +consensus_rpc = "http://testing.prater.beacon-api.nimbus.team" +execution_rpc = "https://eth-goerli.g.alchemy.com:443/v2/o_8Qa9kgwDPf9G8sroyQ-uQtyhyWa3ao" + +[forks] + +[forks.genesis] +epoch = 0 +fork_version = "0x00001020" + +[forks.altair] +epoch = 36660 +fork_version = "0x01001020" + +[forks.bellatrix] +epoch = 112260 +fork_version = "0x02001020" diff --git a/src/client/client.rs b/src/client/client.rs index 4af29c5..4befbaa 100644 --- a/src/client/client.rs +++ b/src/client/client.rs @@ -1,6 +1,9 @@ +use std::sync::Arc; + use ethers::prelude::{Address, U256}; use eyre::Result; +use crate::common::config::Config; use crate::consensus::types::Header; use crate::consensus::ConsensusClient; use crate::execution::evm::Evm; @@ -9,20 +12,23 @@ use crate::execution::ExecutionClient; pub struct Client { consensus: ConsensusClient, execution: ExecutionClient, + config: Arc, } impl Client { - pub async fn new( - consensus_rpc: &str, - execution_rpc: &str, - checkpoint_hash: &str, - ) -> Result { - let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash).await?; + pub async fn new(config: Arc) -> Result { + let consensus_rpc = &config.general.consensus_rpc; + let checkpoint_hash = &config.general.checkpoint; + let execution_rpc = &config.general.execution_rpc; + + let consensus = + ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone()).await?; let execution = ExecutionClient::new(execution_rpc); Ok(Client { consensus, execution, + config, }) } @@ -66,6 +72,10 @@ impl Client { } } + pub fn chain_id(&self) -> u64 { + self.config.general.chain_id + } + pub fn get_header(&self) -> &Header { self.consensus.get_head() } diff --git a/src/client/rpc.rs b/src/client/rpc.rs index b1eda92..c2058ed 100644 --- a/src/client/rpc.rs +++ b/src/client/rpc.rs @@ -1,7 +1,10 @@ -use ethers::{abi::AbiEncode, types::{Address, U256}}; +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 std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc}; use jsonrpsee::{ core::{async_trait, Error}, @@ -9,7 +12,7 @@ use jsonrpsee::{ proc_macros::rpc, }; -use crate::common::utils::hex_str_to_bytes; +use crate::common::utils::{hex_str_to_bytes, u64_to_hex_string}; use super::Client; @@ -46,6 +49,8 @@ trait EthRpc { async fn get_code(&self, address: &str, block: &str) -> Result; #[method(name = "call")] async fn call(&self, opts: CallOpts, block: &str) -> Result; + #[method(name = "chainId")] + fn chain_id(&self) -> Result; } struct RpcInner { @@ -95,14 +100,22 @@ impl EthRpcServer for RpcInner { "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 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())), } } + + fn chain_id(&self) -> Result { + let id = self.client.chain_id(); + Ok(u64_to_hex_string(id)) + } } async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> { diff --git a/src/common/config.rs b/src/common/config.rs new file mode 100644 index 0000000..66f4e80 --- /dev/null +++ b/src/common/config.rs @@ -0,0 +1,64 @@ +use std::{fs, path::Path}; + +use eyre::Result; +use serde::Deserialize; + +use super::utils::hex_str_to_bytes; + +#[derive(Deserialize, Debug)] +pub struct Config { + pub general: General, + pub forks: Forks, +} + +#[derive(Deserialize, Debug)] +pub struct General { + pub chain_id: u64, + #[serde(deserialize_with = "bytes_deserialize")] + pub genesis_root: Vec, + #[serde(deserialize_with = "bytes_deserialize")] + pub checkpoint: Vec, + pub consensus_rpc: String, + pub execution_rpc: String, +} + +#[derive(Deserialize, Debug)] +pub struct Forks { + pub genesis: Fork, + pub altair: Fork, + pub bellatrix: Fork, +} + +#[derive(Deserialize, Debug)] +pub struct Fork { + pub epoch: u64, + #[serde(deserialize_with = "bytes_deserialize")] + pub fork_version: Vec, +} + +impl Config { + pub fn from_file(path: &Path) -> Result { + let contents = fs::read_to_string(path)?; + Ok(toml::from_str(&contents)?) + } + + pub fn fork_version(&self, slot: u64) -> Vec { + let epoch = slot / 32; + + if epoch >= self.forks.bellatrix.epoch { + self.forks.bellatrix.fork_version.clone() + } else if epoch >= self.forks.altair.epoch { + self.forks.altair.fork_version.clone() + } else { + self.forks.genesis.fork_version.clone() + } + } +} + +fn bytes_deserialize<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let bytes: String = serde::Deserialize::deserialize(deserializer)?; + Ok(hex_str_to_bytes(&bytes).unwrap()) +} diff --git a/src/common/mod.rs b/src/common/mod.rs index b5614dd..472f7fc 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -1 +1,2 @@ +pub mod config; pub mod utils; diff --git a/src/consensus/consensus.rs b/src/consensus/consensus.rs index 3466bc5..abbefff 100644 --- a/src/consensus/consensus.rs +++ b/src/consensus/consensus.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use blst::min_pk::{PublicKey, Signature}; use blst::BLST_ERROR; use eyre::Result; @@ -5,11 +7,13 @@ use ssz_rs::prelude::*; use super::rpc::Rpc; use super::types::*; +use crate::common::config::Config; use crate::common::utils::*; pub struct ConsensusClient { rpc: Rpc, store: Store, + config: Arc, } #[derive(Debug)] @@ -20,7 +24,11 @@ struct Store { } impl ConsensusClient { - pub async fn new(nimbus_rpc: &str, checkpoint_block_root: &str) -> Result { + pub async fn new( + nimbus_rpc: &str, + checkpoint_block_root: &Vec, + config: Arc, + ) -> Result { let rpc = Rpc::new(nimbus_rpc); let mut bootstrap = rpc.get_bootstrap(checkpoint_block_root).await?; @@ -32,7 +40,8 @@ impl ConsensusClient { ); let header_hash = bootstrap.header.hash_tree_root()?; - let header_valid = header_hash.to_string() == checkpoint_block_root.to_string(); + let header_valid = + header_hash.to_string() == format!("0x{}", hex::encode(checkpoint_block_root)); if !(header_valid && committee_valid) { return Err(eyre::eyre!("Invalid Bootstrap")); @@ -44,7 +53,7 @@ impl ConsensusClient { next_sync_committee: None, }; - Ok(ConsensusClient { rpc, store }) + Ok(ConsensusClient { rpc, store, config }) } pub async fn get_execution_payload(&self) -> Result { @@ -147,7 +156,7 @@ impl ConsensusClient { } let header_root = bytes_to_bytes32(update.attested_header.hash_tree_root()?.as_bytes()); - let signing_root = compute_committee_sign_root(header_root)?; + let signing_root = self.compute_committee_sign_root(header_root, update.signature_slot)?; let sig = &update.sync_aggregate.sync_committee_signature; let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); @@ -174,6 +183,21 @@ impl ConsensusClient { Some(update.next_sync_committee.as_ref().unwrap().clone()); } } + + fn compute_committee_sign_root(&self, header: Bytes32, slot: u64) -> Result { + let genesis_root = self + .config + .general + .genesis_root + .to_vec() + .try_into() + .unwrap(); + + let domain_type = &hex::decode("07000000")?[..]; + let fork_version = Vector::from_iter(self.config.fork_version(slot)); + let domain = compute_domain(domain_type, fork_version, genesis_root)?; + compute_signing_root(header, domain) + } } fn get_participating_keys( @@ -300,18 +324,6 @@ fn branch_to_nodes(branch: Vec) -> Result> { .collect::>>() } -fn compute_committee_sign_root(header: Bytes32) -> Result { - let genesis_root = - hex::decode("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb")? - .to_vec() - .try_into() - .unwrap(); - let domain_type = &hex::decode("07000000")?[..]; - let fork_version = Vector::from_iter(hex::decode("02001020").unwrap()); - let domain = compute_domain(domain_type, fork_version, genesis_root)?; - compute_signing_root(header, domain) -} - #[derive(SimpleSerialize, Default, Debug)] struct SigningData { object_root: Bytes32, diff --git a/src/consensus/rpc.rs b/src/consensus/rpc.rs index 10d5625..f1f16a3 100644 --- a/src/consensus/rpc.rs +++ b/src/consensus/rpc.rs @@ -13,10 +13,11 @@ impl Rpc { } } - pub async fn get_bootstrap(&self, block_root: &str) -> Result { + pub async fn get_bootstrap(&self, block_root: &Vec) -> Result { + let root_hex = hex::encode(block_root); let req = format!( - "{}/eth/v0/beacon/light_client/bootstrap/{}", - self.rpc, block_root + "{}/eth/v0/beacon/light_client/bootstrap/0x{}", + self.rpc, root_hex ); let res = reqwest::get(req).await?.json::().await?; Ok(res.data.v) diff --git a/src/execution/evm.rs b/src/execution/evm.rs index e5e7acb..0d26f80 100644 --- a/src/execution/evm.rs +++ b/src/execution/evm.rs @@ -74,7 +74,6 @@ impl ProofDB { impl Database for ProofDB { fn basic(&mut self, address: H160) -> AccountInfo { - if is_precompile(&address) { return AccountInfo::default(); } @@ -112,7 +111,6 @@ impl Database for ProofDB { } fn storage(&mut self, address: H160, slot: U256) -> U256 { - let execution = self.execution.clone(); let addr = address.clone(); let slots = [slot]; diff --git a/src/execution/execution.rs b/src/execution/execution.rs index 7209c2b..06a3992 100644 --- a/src/execution/execution.rs +++ b/src/execution/execution.rs @@ -30,7 +30,7 @@ impl ExecutionClient { payload: &ExecutionPayload, ) -> Result { let slots = slots.unwrap_or(&[]); - + let proof = self .rpc .get_proof(&address, slots, payload.block_number) diff --git a/src/execution/proof.rs b/src/execution/proof.rs index f38df2f..05a1738 100644 --- a/src/execution/proof.rs +++ b/src/execution/proof.rs @@ -7,7 +7,6 @@ pub fn verify_proof(proof: &Vec>, root: &Vec, path: &Vec, value: let mut expected_hash = root.clone(); let mut path_offset = 0; - for (i, node) in proof.iter().enumerate() { if expected_hash != keccak256(node).to_vec() { return false; @@ -22,10 +21,11 @@ pub fn verify_proof(proof: &Vec>, root: &Vec, path: &Vec, value: path_offset += 1; } else if node_list.len() == 2 { if i == proof.len() - 1 { - // exclusion proof - if &node_list[0][skip_length(&node_list[0])..] != &path[path_offset..] && value[0] == 0x80 { - return true + if &node_list[0][skip_length(&node_list[0])..] != &path[path_offset..] + && value[0] == 0x80 + { + return true; } // inclusion proof diff --git a/src/main.rs b/src/main.rs index 6b98b28..3d05147 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ -use std::{sync::Arc, time::Duration}; +use std::{path::Path, sync::Arc, time::Duration}; use eyre::Result; +use tokio::time::sleep; use client::{rpc::Rpc, Client}; -use tokio::time::sleep; +use common::config::Config; pub mod client; pub mod common; @@ -12,11 +13,9 @@ pub mod execution; #[tokio::main] async fn main() -> Result<()> { - let consensus_rpc = "http://testing.prater.beacon-api.nimbus.team"; - let execution_rpc = "https://eth-goerli.g.alchemy.com:443/v2/o_8Qa9kgwDPf9G8sroyQ-uQtyhyWa3ao"; - let checkpoint = "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99"; + let config = Config::from_file(Path::new("./configs/goerli.toml"))?; - let mut client = Client::new(consensus_rpc, execution_rpc, checkpoint).await?; + let mut client = Client::new(Arc::new(config)).await?; client.sync().await?; let mut rpc = Rpc::new(Arc::new(client));