add configuration file support
This commit is contained in:
parent
e0beceb8dd
commit
99d7ff4043
|
@ -1609,6 +1609,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"ssz-rs",
|
"ssz-rs",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)> {
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
|
pub mod config;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -30,7 +30,7 @@ impl ExecutionClient {
|
||||||
payload: &ExecutionPayload,
|
payload: &ExecutionPayload,
|
||||||
) -> Result<Account> {
|
) -> Result<Account> {
|
||||||
let slots = slots.unwrap_or(&[]);
|
let slots = slots.unwrap_or(&[]);
|
||||||
|
|
||||||
let proof = self
|
let proof = self
|
||||||
.rpc
|
.rpc
|
||||||
.get_proof(&address, slots, payload.block_number)
|
.get_proof(&address, slots, payload.block_number)
|
||||||
|
|
|
@ -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
|
||||||
|
|
11
src/main.rs
11
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 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));
|
||||||
|
|
Loading…
Reference in New Issue