add configuration file support

This commit is contained in:
Noah Citron 2022-08-26 20:05:12 -04:00
parent e0beceb8dd
commit 99d7ff4043
13 changed files with 163 additions and 43 deletions

1
Cargo.lock generated
View File

@ -1609,6 +1609,7 @@ dependencies = [
"serde", "serde",
"ssz-rs", "ssz-rs",
"tokio", "tokio",
"toml",
] ]
[[package]] [[package]]

View File

@ -18,3 +18,4 @@ jsonrpsee = { version = "0.15.1", features = ["full"] }
revm = "1.9.0" revm = "1.9.0"
bytes = "1.2.1" bytes = "1.2.1"
futures = "0.3.23" futures = "0.3.23"
toml = "0.5.9"

20
configs/goerli.toml Normal file
View File

@ -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"

View File

@ -1,6 +1,9 @@
use std::sync::Arc;
use ethers::prelude::{Address, U256}; use ethers::prelude::{Address, U256};
use eyre::Result; use eyre::Result;
use crate::common::config::Config;
use crate::consensus::types::Header; use crate::consensus::types::Header;
use crate::consensus::ConsensusClient; use crate::consensus::ConsensusClient;
use crate::execution::evm::Evm; use crate::execution::evm::Evm;
@ -9,20 +12,23 @@ use crate::execution::ExecutionClient;
pub struct Client { pub struct Client {
consensus: ConsensusClient, consensus: ConsensusClient,
execution: ExecutionClient, execution: ExecutionClient,
config: Arc<Config>,
} }
impl Client { impl Client {
pub async fn new( pub async fn new(config: Arc<Config>) -> Result<Self> {
consensus_rpc: &str, let consensus_rpc = &config.general.consensus_rpc;
execution_rpc: &str, let checkpoint_hash = &config.general.checkpoint;
checkpoint_hash: &str, let execution_rpc = &config.general.execution_rpc;
) -> Result<Self> {
let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash).await?; let consensus =
ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone()).await?;
let execution = ExecutionClient::new(execution_rpc); let execution = ExecutionClient::new(execution_rpc);
Ok(Client { Ok(Client {
consensus, consensus,
execution, 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 { pub fn get_header(&self) -> &Header {
self.consensus.get_head() self.consensus.get_head()
} }

View File

@ -1,7 +1,10 @@
use ethers::{abi::AbiEncode, types::{Address, U256}}; use ethers::{
abi::AbiEncode,
types::{Address, U256},
};
use eyre::Result; use eyre::Result;
use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
use jsonrpsee::{ use jsonrpsee::{
core::{async_trait, Error}, core::{async_trait, Error},
@ -9,7 +12,7 @@ use jsonrpsee::{
proc_macros::rpc, 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; use super::Client;
@ -46,6 +49,8 @@ trait EthRpc {
async fn get_code(&self, address: &str, block: &str) -> Result<String, Error>; async fn get_code(&self, address: &str, block: &str) -> Result<String, Error>;
#[method(name = "call")] #[method(name = "call")]
async fn call(&self, opts: CallOpts, block: &str) -> Result<String, Error>; async fn call(&self, opts: CallOpts, block: &str) -> Result<String, Error>;
#[method(name = "chainId")]
fn chain_id(&self) -> Result<String, Error>;
} }
struct RpcInner { struct RpcInner {
@ -95,14 +100,22 @@ impl EthRpcServer for RpcInner {
"latest" => { "latest" => {
let to = convert_err(Address::from_str(&opts.to))?; 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 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)?; let res = convert_err(self.client.call(&to, &data, value).await)?;
Ok(hex::encode(res)) Ok(hex::encode(res))
}, }
_ => Err(Error::Custom("Invalid Block Number".to_string())), _ => Err(Error::Custom("Invalid Block Number".to_string())),
} }
} }
fn chain_id(&self) -> Result<String, Error> {
let id = self.client.chain_id();
Ok(u64_to_hex_string(id))
}
} }
async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> { async fn start(rpc: RpcInner) -> Result<(HttpServerHandle, SocketAddr)> {

64
src/common/config.rs Normal file
View File

@ -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<u8>,
#[serde(deserialize_with = "bytes_deserialize")]
pub checkpoint: Vec<u8>,
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<u8>,
}
impl Config {
pub fn from_file(path: &Path) -> Result<Self> {
let contents = fs::read_to_string(path)?;
Ok(toml::from_str(&contents)?)
}
pub fn fork_version(&self, slot: u64) -> Vec<u8> {
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<Vec<u8>, D::Error>
where
D: serde::Deserializer<'de>,
{
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
Ok(hex_str_to_bytes(&bytes).unwrap())
}

View File

@ -1 +1,2 @@
pub mod config;
pub mod utils; pub mod utils;

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use blst::min_pk::{PublicKey, Signature}; use blst::min_pk::{PublicKey, Signature};
use blst::BLST_ERROR; use blst::BLST_ERROR;
use eyre::Result; use eyre::Result;
@ -5,11 +7,13 @@ use ssz_rs::prelude::*;
use super::rpc::Rpc; use super::rpc::Rpc;
use super::types::*; use super::types::*;
use crate::common::config::Config;
use crate::common::utils::*; use crate::common::utils::*;
pub struct ConsensusClient { pub struct ConsensusClient {
rpc: Rpc, rpc: Rpc,
store: Store, store: Store,
config: Arc<Config>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -20,7 +24,11 @@ struct Store {
} }
impl ConsensusClient { impl ConsensusClient {
pub async fn new(nimbus_rpc: &str, checkpoint_block_root: &str) -> Result<ConsensusClient> { pub async fn new(
nimbus_rpc: &str,
checkpoint_block_root: &Vec<u8>,
config: Arc<Config>,
) -> Result<ConsensusClient> {
let rpc = Rpc::new(nimbus_rpc); let rpc = Rpc::new(nimbus_rpc);
let mut bootstrap = rpc.get_bootstrap(checkpoint_block_root).await?; 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_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) { if !(header_valid && committee_valid) {
return Err(eyre::eyre!("Invalid Bootstrap")); return Err(eyre::eyre!("Invalid Bootstrap"));
@ -44,7 +53,7 @@ impl ConsensusClient {
next_sync_committee: None, next_sync_committee: None,
}; };
Ok(ConsensusClient { rpc, store }) Ok(ConsensusClient { rpc, store, config })
} }
pub async fn get_execution_payload(&self) -> Result<ExecutionPayload> { pub async fn get_execution_payload(&self) -> Result<ExecutionPayload> {
@ -147,7 +156,7 @@ impl ConsensusClient {
} }
let header_root = bytes_to_bytes32(update.attested_header.hash_tree_root()?.as_bytes()); 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 sig = &update.sync_aggregate.sync_committee_signature;
let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); 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()); Some(update.next_sync_committee.as_ref().unwrap().clone());
} }
} }
fn compute_committee_sign_root(&self, header: Bytes32, slot: u64) -> Result<Node> {
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( fn get_participating_keys(
@ -300,18 +324,6 @@ fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
.collect::<Result<Vec<Node>>>() .collect::<Result<Vec<Node>>>()
} }
fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
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)] #[derive(SimpleSerialize, Default, Debug)]
struct SigningData { struct SigningData {
object_root: Bytes32, object_root: Bytes32,

View File

@ -13,10 +13,11 @@ impl Rpc {
} }
} }
pub async fn get_bootstrap(&self, block_root: &str) -> Result<Bootstrap> { pub async fn get_bootstrap(&self, block_root: &Vec<u8>) -> Result<Bootstrap> {
let root_hex = hex::encode(block_root);
let req = format!( let req = format!(
"{}/eth/v0/beacon/light_client/bootstrap/{}", "{}/eth/v0/beacon/light_client/bootstrap/0x{}",
self.rpc, block_root self.rpc, root_hex
); );
let res = reqwest::get(req).await?.json::<BootstrapResponse>().await?; let res = reqwest::get(req).await?.json::<BootstrapResponse>().await?;
Ok(res.data.v) Ok(res.data.v)

View File

@ -74,7 +74,6 @@ impl ProofDB {
impl Database for ProofDB { impl Database for ProofDB {
fn basic(&mut self, address: H160) -> AccountInfo { fn basic(&mut self, address: H160) -> AccountInfo {
if is_precompile(&address) { if is_precompile(&address) {
return AccountInfo::default(); return AccountInfo::default();
} }
@ -112,7 +111,6 @@ impl Database for ProofDB {
} }
fn storage(&mut self, address: H160, slot: U256) -> U256 { fn storage(&mut self, address: H160, slot: U256) -> U256 {
let execution = self.execution.clone(); let execution = self.execution.clone();
let addr = address.clone(); let addr = address.clone();
let slots = [slot]; let slots = [slot];

View File

@ -7,7 +7,6 @@ pub fn verify_proof(proof: &Vec<Vec<u8>>, root: &Vec<u8>, path: &Vec<u8>, value:
let mut expected_hash = root.clone(); let mut expected_hash = root.clone();
let mut path_offset = 0; let mut path_offset = 0;
for (i, node) in proof.iter().enumerate() { for (i, node) in proof.iter().enumerate() {
if expected_hash != keccak256(node).to_vec() { if expected_hash != keccak256(node).to_vec() {
return false; return false;
@ -22,10 +21,11 @@ pub fn verify_proof(proof: &Vec<Vec<u8>>, root: &Vec<u8>, path: &Vec<u8>, value:
path_offset += 1; path_offset += 1;
} else if node_list.len() == 2 { } else if node_list.len() == 2 {
if i == proof.len() - 1 { if i == proof.len() - 1 {
// exclusion proof // exclusion proof
if &node_list[0][skip_length(&node_list[0])..] != &path[path_offset..] && value[0] == 0x80 { if &node_list[0][skip_length(&node_list[0])..] != &path[path_offset..]
return true && value[0] == 0x80
{
return true;
} }
// inclusion proof // inclusion proof

View File

@ -1,9 +1,10 @@
use std::{sync::Arc, time::Duration}; use std::{path::Path, sync::Arc, time::Duration};
use eyre::Result; use eyre::Result;
use tokio::time::sleep;
use client::{rpc::Rpc, Client}; use client::{rpc::Rpc, Client};
use tokio::time::sleep; use common::config::Config;
pub mod client; pub mod client;
pub mod common; pub mod common;
@ -12,11 +13,9 @@ pub mod execution;
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let consensus_rpc = "http://testing.prater.beacon-api.nimbus.team"; let config = Config::from_file(Path::new("./configs/goerli.toml"))?;
let execution_rpc = "https://eth-goerli.g.alchemy.com:443/v2/o_8Qa9kgwDPf9G8sroyQ-uQtyhyWa3ao";
let checkpoint = "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99";
let mut client = Client::new(consensus_rpc, execution_rpc, checkpoint).await?; let mut client = Client::new(Arc::new(config)).await?;
client.sync().await?; client.sync().await?;
let mut rpc = Rpc::new(Arc::new(client)); let mut rpc = Rpc::new(Arc::new(client));