refactor: clean up (#78)

* refactor client rpc

* refactor node

* remove unused deps

* remove unused import

* refactor consensus

* consensus refactor

* rename rpc traits

* refactor execution
This commit is contained in:
Noah Citron 2022-11-01 23:52:28 -04:00 committed by GitHub
parent d06fb73803
commit a9b34f3dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 190 additions and 204 deletions

19
Cargo.lock generated
View File

@ -446,7 +446,6 @@ dependencies = [
"hex", "hex",
"jsonrpsee", "jsonrpsee",
"log", "log",
"openssl",
"reqwest", "reqwest",
"revm", "revm",
"serde", "serde",
@ -519,11 +518,9 @@ dependencies = [
"ethers", "ethers",
"eyre", "eyre",
"hex", "hex",
"openssl",
"serde", "serde",
"ssz-rs", "ssz-rs",
"thiserror", "thiserror",
"toml",
] ]
[[package]] [[package]]
@ -552,13 +549,9 @@ dependencies = [
"config", "config",
"ethers", "ethers",
"eyre", "eyre",
"futures",
"hex", "hex",
"jsonrpsee",
"log", "log",
"openssl",
"reqwest", "reqwest",
"revm",
"serde", "serde",
"serde_json", "serde_json",
"ssz-rs", "ssz-rs",
@ -1044,9 +1037,7 @@ dependencies = [
"eyre", "eyre",
"futures", "futures",
"hex", "hex",
"jsonrpsee",
"log", "log",
"openssl",
"reqwest", "reqwest",
"revm", "revm",
"serde", "serde",
@ -2231,15 +2222,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "111.22.0+1.1.1q"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.75" version = "0.9.75"
@ -2249,7 +2231,6 @@ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
"libc", "libc",
"openssl-src",
"pkg-config", "pkg-config",
"vcpkg", "vcpkg",
] ]

View File

@ -20,7 +20,6 @@ bytes = "1.2.1"
futures = "0.3.23" futures = "0.3.23"
toml = "0.5.9" toml = "0.5.9"
log = "0.4.17" log = "0.4.17"
openssl = { version = "0.10", features = ["vendored"] }
common = { path = "../common" } common = { path = "../common" }
consensus = { path = "../consensus" } consensus = { path = "../consensus" }

View File

@ -90,6 +90,8 @@ impl Node {
self.payloads.pop_first(); self.payloads.pop_first();
} }
// only save one finalized block per epoch
// finality updates only occur on epoch boundries
while self.finalized_payloads.len() > usize::max(self.history_size / 32, 1) { while self.finalized_payloads.len() > usize::max(self.history_size / 32, 1) {
self.finalized_payloads.pop_first(); self.finalized_payloads.pop_first();
} }
@ -172,6 +174,7 @@ impl Node {
.await .await
} }
// assumes tip of 1 gwei to prevent having to prove out every tx in the block
pub fn get_gas_price(&self) -> Result<U256> { pub fn get_gas_price(&self) -> Result<U256> {
self.check_head_age()?; self.check_head_age()?;
@ -181,6 +184,7 @@ impl Node {
Ok(base_fee + tip) Ok(base_fee + tip)
} }
// assumes tip of 1 gwei to prevent having to prove out every tx in the block
pub fn get_priority_fee(&self) -> Result<U256> { pub fn get_priority_fee(&self) -> Result<U256> {
let tip = U256::from(10_u64.pow(9)); let tip = U256::from(10_u64.pow(9));
Ok(tip) Ok(tip)
@ -221,13 +225,13 @@ impl Node {
.filter(|entry| &entry.1.block_hash.to_vec() == hash) .filter(|entry| &entry.1.block_hash.to_vec() == hash)
.collect::<Vec<(&u64, &ExecutionPayload)>>(); .collect::<Vec<(&u64, &ExecutionPayload)>>();
match payloads.get(0) { if let Some(payload_entry) = payloads.get(0) {
Some(payload_entry) => self self.execution
.execution
.get_block(payload_entry.1, full_tx) .get_block(payload_entry.1, full_tx)
.await .await
.map(|b| Some(b)), .map(|b| Some(b))
None => Ok(None), } else {
Ok(None)
} }
} }

View File

@ -3,7 +3,7 @@ use ethers::{
types::{Address, Transaction, TransactionReceipt, H256}, types::{Address, Transaction, TransactionReceipt, H256},
}; };
use eyre::Result; use eyre::Result;
use log::{debug, info, warn}; use log::{info, warn};
use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc}; use std::{fmt::Display, net::SocketAddr, str::FromStr, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -108,7 +108,6 @@ struct RpcInner {
#[async_trait] #[async_trait]
impl EthRpcServer for RpcInner { impl EthRpcServer for RpcInner {
async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> { async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> {
debug!("eth_getBalance");
let address = convert_err(Address::from_str(address))?; let address = convert_err(Address::from_str(address))?;
let node = self.node.read().await; let node = self.node.read().await;
let balance = convert_err(node.get_balance(&address, &block).await)?; let balance = convert_err(node.get_balance(&address, &block).await)?;
@ -133,7 +132,6 @@ impl EthRpcServer for RpcInner {
} }
async fn call(&self, opts: CallOpts, block: BlockTag) -> Result<String, Error> { async fn call(&self, opts: CallOpts, block: BlockTag) -> Result<String, Error> {
debug!("eth_call");
let node = self.node.read().await; let node = self.node.read().await;
let res = convert_err(node.call(&opts, &block))?; let res = convert_err(node.call(&opts, &block))?;
@ -141,7 +139,6 @@ impl EthRpcServer for RpcInner {
} }
async fn estimate_gas(&self, opts: CallOpts) -> Result<String, Error> { async fn estimate_gas(&self, opts: CallOpts) -> Result<String, Error> {
debug!("eth_estimateGas");
let node = self.node.read().await; let node = self.node.read().await;
let gas = convert_err(node.estimate_gas(&opts))?; let gas = convert_err(node.estimate_gas(&opts))?;

View File

@ -11,6 +11,4 @@ serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3" hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" } ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" }
ethers = "0.17.0" ethers = "0.17.0"
toml = "0.5.9"
openssl = { version = "0.10", features = ["vendored"] }
thiserror = "1.0.37" thiserror = "1.0.37"

View File

@ -15,15 +15,11 @@ hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" } ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" }
blst = "0.3.10" blst = "0.3.10"
ethers = "0.17.0" ethers = "0.17.0"
jsonrpsee = { version = "0.15.1", features = ["full"] }
revm = "1.9.0"
bytes = "1.2.1" bytes = "1.2.1"
futures = "0.3.23"
toml = "0.5.9" toml = "0.5.9"
async-trait = "0.1.57" async-trait = "0.1.57"
log = "0.4.17" log = "0.4.17"
chrono = "0.4.22" chrono = "0.4.22"
openssl = { version = "0.10", features = ["vendored"] }
thiserror = "1.0.37" thiserror = "1.0.37"
common = { path = "../common" } common = { path = "../common" }

View File

@ -2,8 +2,7 @@ use std::cmp;
use std::sync::Arc; use std::sync::Arc;
use std::time::UNIX_EPOCH; use std::time::UNIX_EPOCH;
use blst::min_pk::{PublicKey, Signature}; use blst::min_pk::PublicKey;
use blst::BLST_ERROR;
use chrono::Duration; use chrono::Duration;
use eyre::eyre; use eyre::eyre;
use eyre::Result; use eyre::Result;
@ -16,18 +15,22 @@ use config::Config;
use crate::errors::ConsensusError; use crate::errors::ConsensusError;
use super::rpc::Rpc; use super::rpc::ConsensusRpc;
use super::types::*; use super::types::*;
use super::utils::*;
pub struct ConsensusClient<R: Rpc> { // https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md
// does not implement force updates
pub struct ConsensusClient<R: ConsensusRpc> {
rpc: R, rpc: R,
store: Store, store: LightClientStore,
pub last_checkpoint: Option<Vec<u8>>, pub last_checkpoint: Option<Vec<u8>>,
pub config: Arc<Config>, pub config: Arc<Config>,
} }
#[derive(Debug)] #[derive(Debug)]
struct Store { struct LightClientStore {
finalized_header: Header, finalized_header: Header,
current_sync_committee: SyncCommittee, current_sync_committee: SyncCommittee,
next_sync_committee: Option<SyncCommittee>, next_sync_committee: Option<SyncCommittee>,
@ -36,7 +39,7 @@ struct Store {
current_max_active_participants: u64, current_max_active_participants: u64,
} }
impl<R: Rpc> ConsensusClient<R> { impl<R: ConsensusRpc> ConsensusClient<R> {
pub async fn new( pub async fn new(
rpc: &str, rpc: &str,
checkpoint_block_root: &Vec<u8>, checkpoint_block_root: &Vec<u8>,
@ -67,7 +70,7 @@ impl<R: Rpc> ConsensusClient<R> {
return Err(ConsensusError::InvalidCurrentSyncCommitteeProof.into()); return Err(ConsensusError::InvalidCurrentSyncCommitteeProof.into());
} }
let store = Store { let store = LightClientStore {
finalized_header: bootstrap.header.clone(), finalized_header: bootstrap.header.clone(),
current_sync_committee: bootstrap.current_sync_committee, current_sync_committee: bootstrap.current_sync_committee,
next_sync_committee: None, next_sync_committee: None,
@ -167,6 +170,8 @@ impl<R: Rpc> ConsensusClient<R> {
Ok(()) Ok(())
} }
// implements checks from validate_light_client_update and process_light_client_update in the
// specification
fn verify_generic_update(&self, update: &GenericUpdate) -> Result<()> { fn verify_generic_update(&self, update: &GenericUpdate) -> Result<()> {
let bits = get_bits(&update.sync_aggregate.sync_committee_bits); let bits = get_bits(&update.sync_aggregate.sync_committee_bits);
if bits == 0 { if bits == 0 {
@ -237,13 +242,13 @@ impl<R: Rpc> ConsensusClient<R> {
let pks = let pks =
get_participating_keys(sync_committee, &update.sync_aggregate.sync_committee_bits)?; get_participating_keys(sync_committee, &update.sync_aggregate.sync_committee_bits)?;
let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect();
let header_root = let is_valid_sig = self.verify_sync_committee_signture(
bytes_to_bytes32(update.attested_header.clone().hash_tree_root()?.as_bytes()); &pks,
let signing_root = self.compute_committee_sign_root(header_root, update.signature_slot)?; &update.attested_header,
let sig = &update.sync_aggregate.sync_committee_signature; &update.sync_aggregate.sync_committee_signature,
let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); update.signature_slot,
);
if !is_valid_sig { if !is_valid_sig {
return Err(ConsensusError::InvalidSignature.into()); return Err(ConsensusError::InvalidSignature.into());
@ -267,13 +272,15 @@ impl<R: Rpc> ConsensusClient<R> {
self.verify_generic_update(&update) self.verify_generic_update(&update)
} }
// implements state changes from apply_light_client_update and process_light_client_update in
// the specification
fn apply_generic_update(&mut self, update: &GenericUpdate) { fn apply_generic_update(&mut self, update: &GenericUpdate) {
let committee_bits = get_bits(&update.sync_aggregate.sync_committee_bits); let committee_bits = get_bits(&update.sync_aggregate.sync_committee_bits);
self.store.current_max_active_participants = self.store.current_max_active_participants =
u64::max(self.store.current_max_active_participants, committee_bits); u64::max(self.store.current_max_active_participants, committee_bits);
let should_update_optimistic = committee_bits > self.safety_theshhold() let should_update_optimistic = committee_bits > self.safety_threshold()
&& update.attested_header.slot > self.store.optimistic_header.slot; && update.attested_header.slot > self.store.optimistic_header.slot;
if should_update_optimistic { if should_update_optimistic {
@ -393,13 +400,36 @@ impl<R: Rpc> ConsensusClient<R> {
update.next_sync_committee.is_some() && update.next_sync_committee_branch.is_some() update.next_sync_committee.is_some() && update.next_sync_committee_branch.is_some()
} }
fn safety_theshhold(&self) -> u64 { fn safety_threshold(&self) -> u64 {
cmp::max( cmp::max(
self.store.current_max_active_participants, self.store.current_max_active_participants,
self.store.previous_max_active_participants, self.store.previous_max_active_participants,
) / 2 ) / 2
} }
fn verify_sync_committee_signture(
&self,
pks: &Vec<PublicKey>,
attested_header: &Header,
signature: &SignatureBytes,
signature_slot: u64,
) -> bool {
let res: Result<bool> = (move || {
let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect();
let header_root =
bytes_to_bytes32(attested_header.clone().hash_tree_root()?.as_bytes());
let signing_root = self.compute_committee_sign_root(header_root, signature_slot)?;
Ok(is_aggregate_valid(signature, signing_root.as_bytes(), &pks))
})();
if let Ok(is_valid) = res {
is_valid
} else {
false
}
}
fn compute_committee_sign_root(&self, header: Bytes32, slot: u64) -> Result<Node> { fn compute_committee_sign_root(&self, header: Bytes32, slot: u64) -> Result<Node> {
let genesis_root = self.config.chain.genesis_root.to_vec().try_into().unwrap(); let genesis_root = self.config.chain.genesis_root.to_vec().try_into().unwrap();
@ -479,42 +509,12 @@ fn get_bits(bitfield: &Bitvector<512>) -> u64 {
count count
} }
fn is_aggregate_valid(sig_bytes: &SignatureBytes, msg: &[u8], pks: &[&PublicKey]) -> bool {
let dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
let sig_res = Signature::from_bytes(&sig_bytes);
match sig_res {
Ok(sig) => sig.fast_aggregate_verify(true, msg, dst, &pks) == BLST_ERROR::BLST_SUCCESS,
Err(_) => false,
}
}
fn is_finality_proof_valid( fn is_finality_proof_valid(
attested_header: &Header, attested_header: &Header,
finality_header: &mut Header, finality_header: &mut Header,
finality_branch: &Vec<Bytes32>, finality_branch: &Vec<Bytes32>,
) -> bool { ) -> bool {
let finality_header_hash_res = finality_header.hash_tree_root(); is_proof_valid(attested_header, finality_header, finality_branch, 6, 41)
if finality_header_hash_res.is_err() {
return false;
}
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
if attested_header_state_root_res.is_err() {
return false;
}
let finality_branch_res = branch_to_nodes(finality_branch.clone());
if finality_branch_res.is_err() {
return false;
}
is_valid_merkle_branch(
&finality_header_hash_res.unwrap(),
finality_branch_res.unwrap().iter(),
6,
41,
&attested_header_state_root_res.unwrap(),
)
} }
fn is_next_committee_proof_valid( fn is_next_committee_proof_valid(
@ -522,27 +522,12 @@ fn is_next_committee_proof_valid(
next_committee: &mut SyncCommittee, next_committee: &mut SyncCommittee,
next_committee_branch: &Vec<Bytes32>, next_committee_branch: &Vec<Bytes32>,
) -> bool { ) -> bool {
let next_committee_hash_res = next_committee.hash_tree_root(); is_proof_valid(
if next_committee_hash_res.is_err() { attested_header,
return false; next_committee,
} next_committee_branch,
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
if attested_header_state_root_res.is_err() {
return false;
}
let next_committee_branch_res = branch_to_nodes(next_committee_branch.clone());
if next_committee_branch_res.is_err() {
return false;
}
is_valid_merkle_branch(
&next_committee_hash_res.unwrap(),
next_committee_branch_res.unwrap().iter(),
5, 5,
23, 23,
&attested_header_state_root_res.unwrap(),
) )
} }
@ -551,86 +536,15 @@ fn is_current_committee_proof_valid(
current_committee: &mut SyncCommittee, current_committee: &mut SyncCommittee,
current_committee_branch: &Vec<Bytes32>, current_committee_branch: &Vec<Bytes32>,
) -> bool { ) -> bool {
let next_committee_hash_res = current_committee.hash_tree_root(); is_proof_valid(
if next_committee_hash_res.is_err() { attested_header,
return false; current_committee,
} current_committee_branch,
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
if attested_header_state_root_res.is_err() {
return false;
}
let next_committee_branch_res = branch_to_nodes(current_committee_branch.clone());
if next_committee_branch_res.is_err() {
return false;
}
is_valid_merkle_branch(
&next_committee_hash_res.unwrap(),
next_committee_branch_res.unwrap().iter(),
5, 5,
22, 22,
&attested_header_state_root_res.unwrap(),
) )
} }
fn calc_sync_period(slot: u64) -> u64 {
let epoch = slot / 32;
epoch / 256
}
fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
branch
.iter()
.map(|elem| bytes32_to_node(elem))
.collect::<Result<Vec<Node>>>()
}
#[derive(SimpleSerialize, Default, Debug)]
struct SigningData {
object_root: Bytes32,
domain: Bytes32,
}
#[derive(SimpleSerialize, Default, Debug)]
struct ForkData {
current_version: Vector<u8, 4>,
genesis_validator_root: Bytes32,
}
fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result<Node> {
let mut data = SigningData {
object_root,
domain,
};
Ok(data.hash_tree_root()?)
}
fn compute_domain(
domain_type: &[u8],
fork_version: Vector<u8, 4>,
genesis_root: Bytes32,
) -> Result<Bytes32> {
let fork_data_root = compute_fork_data_root(fork_version, genesis_root)?;
let start = domain_type;
let end = &fork_data_root.as_bytes()[..28];
let d = [start, end].concat();
Ok(d.to_vec().try_into().unwrap())
}
fn compute_fork_data_root(
current_version: Vector<u8, 4>,
genesis_validator_root: Bytes32,
) -> Result<Node> {
let current_version = current_version.try_into()?;
let mut fork_data = ForkData {
current_version,
genesis_validator_root,
};
Ok(fork_data.hash_tree_root()?)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc; use std::sync::Arc;
@ -640,7 +554,7 @@ mod tests {
use crate::{ use crate::{
consensus::calc_sync_period, consensus::calc_sync_period,
errors::ConsensusError, errors::ConsensusError,
rpc::{mock_rpc::MockRpc, Rpc}, rpc::{mock_rpc::MockRpc, ConsensusRpc},
types::Header, types::Header,
ConsensusClient, ConsensusClient,
}; };
@ -688,7 +602,7 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn test_verify_upadate_invlaid_finality() { async fn test_verify_update_invalid_finality() {
let client = get_client().await; let client = get_client().await;
let period = calc_sync_period(client.store.finalized_header.slot); let period = calc_sync_period(client.store.finalized_header.slot);
let updates = client.rpc.get_updates(period).await.unwrap(); let updates = client.rpc.get_updates(period).await.unwrap();
@ -730,7 +644,7 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn test_verify_finality_invlaid_finality() { async fn test_verify_finality_invalid_finality() {
let mut client = get_client().await; let mut client = get_client().await;
client.sync().await.unwrap(); client.sync().await.unwrap();
@ -745,7 +659,7 @@ mod tests {
} }
#[tokio::test] #[tokio::test]
async fn test_verify_finality_invlaid_sig() { async fn test_verify_finality_invalid_sig() {
let mut client = get_client().await; let mut client = get_client().await;
client.sync().await.unwrap(); client.sync().await.unwrap();

View File

@ -4,3 +4,5 @@ pub mod types;
mod consensus; mod consensus;
pub use crate::consensus::*; pub use crate::consensus::*;
mod utils;

View File

@ -3,7 +3,7 @@ use std::{fs::read_to_string, path::PathBuf};
use async_trait::async_trait; use async_trait::async_trait;
use eyre::Result; use eyre::Result;
use super::Rpc; use super::ConsensusRpc;
use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Update}; use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Update};
pub struct MockRpc { pub struct MockRpc {
@ -11,7 +11,7 @@ pub struct MockRpc {
} }
#[async_trait] #[async_trait]
impl Rpc for MockRpc { impl ConsensusRpc for MockRpc {
fn new(path: &str) -> Self { fn new(path: &str) -> Self {
MockRpc { MockRpc {
testdata: PathBuf::from(path), testdata: PathBuf::from(path),

View File

@ -6,8 +6,9 @@ use eyre::Result;
use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Update}; use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Update};
// implements https://github.com/ethereum/beacon-APIs/tree/master/apis/beacon/light_client
#[async_trait] #[async_trait]
pub trait Rpc { pub trait ConsensusRpc {
fn new(path: &str) -> Self; fn new(path: &str) -> Self;
async fn get_bootstrap(&self, block_root: &Vec<u8>) -> Result<Bootstrap>; async fn get_bootstrap(&self, block_root: &Vec<u8>) -> Result<Bootstrap>;
async fn get_updates(&self, period: u64) -> Result<Vec<Update>>; async fn get_updates(&self, period: u64) -> Result<Vec<Update>>;

View File

@ -2,7 +2,7 @@ use async_trait::async_trait;
use common::errors::RpcError; use common::errors::RpcError;
use eyre::Result; use eyre::Result;
use super::Rpc; use super::ConsensusRpc;
use crate::types::*; use crate::types::*;
pub struct NimbusRpc { pub struct NimbusRpc {
@ -10,7 +10,7 @@ pub struct NimbusRpc {
} }
#[async_trait] #[async_trait]
impl Rpc for NimbusRpc { impl ConsensusRpc for NimbusRpc {
fn new(rpc: &str) -> Self { fn new(rpc: &str) -> Self {
NimbusRpc { NimbusRpc {
rpc: rpc.to_string(), rpc: rpc.to_string(),

97
consensus/src/utils.rs Normal file
View File

@ -0,0 +1,97 @@
use blst::{
min_pk::{PublicKey, Signature},
BLST_ERROR,
};
use common::{types::Bytes32, utils::bytes32_to_node};
use eyre::Result;
use ssz_rs::prelude::*;
use crate::types::{Header, SignatureBytes};
pub fn calc_sync_period(slot: u64) -> u64 {
let epoch = slot / 32; // 32 slots per epoch
epoch / 256 // 256 epochs per sync committee
}
pub fn is_aggregate_valid(sig_bytes: &SignatureBytes, msg: &[u8], pks: &[&PublicKey]) -> bool {
let dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
let sig_res = Signature::from_bytes(&sig_bytes);
match sig_res {
Ok(sig) => sig.fast_aggregate_verify(true, msg, dst, &pks) == BLST_ERROR::BLST_SUCCESS,
Err(_) => false,
}
}
pub fn is_proof_valid<L: Merkleized>(
attested_header: &Header,
leaf_object: &mut L,
branch: &Vec<Bytes32>,
depth: usize,
index: usize,
) -> bool {
let res: Result<bool> = (move || {
let leaf_hash = leaf_object.hash_tree_root()?;
let state_root = bytes32_to_node(&attested_header.state_root)?;
let branch = branch_to_nodes(branch.to_vec())?;
let is_valid = is_valid_merkle_branch(&leaf_hash, branch.iter(), depth, index, &state_root);
Ok(is_valid)
})();
if let Ok(is_valid) = res {
is_valid
} else {
false
}
}
#[derive(SimpleSerialize, Default, Debug)]
struct SigningData {
object_root: Bytes32,
domain: Bytes32,
}
#[derive(SimpleSerialize, Default, Debug)]
struct ForkData {
current_version: Vector<u8, 4>,
genesis_validator_root: Bytes32,
}
pub fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result<Node> {
let mut data = SigningData {
object_root,
domain,
};
Ok(data.hash_tree_root()?)
}
pub fn compute_domain(
domain_type: &[u8],
fork_version: Vector<u8, 4>,
genesis_root: Bytes32,
) -> Result<Bytes32> {
let fork_data_root = compute_fork_data_root(fork_version, genesis_root)?;
let start = domain_type;
let end = &fork_data_root.as_bytes()[..28];
let d = [start, end].concat();
Ok(d.to_vec().try_into().unwrap())
}
fn compute_fork_data_root(
current_version: Vector<u8, 4>,
genesis_validator_root: Bytes32,
) -> Result<Node> {
let current_version = current_version.try_into()?;
let mut fork_data = ForkData {
current_version,
genesis_validator_root,
};
Ok(fork_data.hash_tree_root()?)
}
pub fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
branch
.iter()
.map(|elem| bytes32_to_node(elem))
.collect::<Result<Vec<Node>>>()
}

View File

@ -15,14 +15,12 @@ hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" } ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs" }
blst = "0.3.10" blst = "0.3.10"
ethers = "0.17.0" ethers = "0.17.0"
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" toml = "0.5.9"
triehash-ethereum = { git = "https://github.com/openethereum/parity-ethereum" } triehash-ethereum = { git = "https://github.com/openethereum/parity-ethereum" }
async-trait = "0.1.57" async-trait = "0.1.57"
openssl = { version = "0.10", features = ["vendored"] }
log = "0.4.17" log = "0.4.17"
thiserror = "1.0.37" thiserror = "1.0.37"

View File

@ -15,18 +15,18 @@ use tokio::runtime::Runtime;
use consensus::types::ExecutionPayload; use consensus::types::ExecutionPayload;
use crate::{ use crate::{
rpc::Rpc, rpc::ExecutionRpc,
types::{Account, CallOpts}, types::{Account, CallOpts},
}; };
use super::ExecutionClient; use super::ExecutionClient;
pub struct Evm<R: Rpc> { pub struct Evm<R: ExecutionRpc> {
evm: EVM<ProofDB<R>>, evm: EVM<ProofDB<R>>,
chain_id: u64, chain_id: u64,
} }
impl<R: Rpc> Evm<R> { impl<R: ExecutionRpc> Evm<R> {
pub fn new(execution: ExecutionClient<R>, payload: ExecutionPayload, chain_id: u64) -> Self { pub fn new(execution: ExecutionClient<R>, payload: ExecutionPayload, chain_id: u64) -> Self {
let mut evm: EVM<ProofDB<R>> = EVM::new(); let mut evm: EVM<ProofDB<R>> = EVM::new();
let db = ProofDB::new(execution, payload); let db = ProofDB::new(execution, payload);
@ -64,6 +64,7 @@ impl<R: Rpc> Evm<R> {
return Err(eyre::eyre!(err.clone())); return Err(eyre::eyre!(err.clone()));
} }
// overestimate to avoid out of gas reverts
let gas_scaled = (1.10 * gas as f64) as u64; let gas_scaled = (1.10 * gas as f64) as u64;
Ok(gas_scaled) Ok(gas_scaled)
} }
@ -158,14 +159,14 @@ impl<R: Rpc> Evm<R> {
} }
} }
struct ProofDB<R: Rpc> { struct ProofDB<R: ExecutionRpc> {
execution: ExecutionClient<R>, execution: ExecutionClient<R>,
payload: ExecutionPayload, payload: ExecutionPayload,
accounts: HashMap<Address, Account>, accounts: HashMap<Address, Account>,
error: Option<String>, error: Option<String>,
} }
impl<R: Rpc> ProofDB<R> { impl<R: ExecutionRpc> ProofDB<R> {
pub fn new(execution: ExecutionClient<R>, payload: ExecutionPayload) -> Self { pub fn new(execution: ExecutionClient<R>, payload: ExecutionPayload) -> Self {
ProofDB { ProofDB {
execution, execution,
@ -205,7 +206,7 @@ impl<R: Rpc> ProofDB<R> {
} }
} }
impl<R: Rpc> Database for ProofDB<R> { impl<R: ExecutionRpc> Database for ProofDB<R> {
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();

View File

@ -18,17 +18,17 @@ use crate::errors::ExecutionError;
use crate::types::Transactions; use crate::types::Transactions;
use super::proof::{encode_account, verify_proof}; use super::proof::{encode_account, verify_proof};
use super::rpc::Rpc; use super::rpc::ExecutionRpc;
use super::types::{Account, ExecutionBlock}; use super::types::{Account, ExecutionBlock};
#[derive(Clone)] #[derive(Clone)]
pub struct ExecutionClient<R: Rpc> { pub struct ExecutionClient<R: ExecutionRpc> {
pub rpc: R, pub rpc: R,
} }
impl<R: Rpc> ExecutionClient<R> { impl<R: ExecutionRpc> ExecutionClient<R> {
pub fn new(rpc: &str) -> Result<Self> { pub fn new(rpc: &str) -> Result<Self> {
let rpc = Rpc::new(rpc)?; let rpc = ExecutionRpc::new(rpc)?;
Ok(ExecutionClient { rpc }) Ok(ExecutionClient { rpc })
} }

View File

@ -11,11 +11,10 @@ use ethers::types::{
TransactionReceipt, H256, U256, TransactionReceipt, H256, U256,
}; };
use eyre::Result; use eyre::Result;
use log::trace;
use crate::types::CallOpts; use crate::types::CallOpts;
use super::Rpc; use super::ExecutionRpc;
pub struct HttpRpc { pub struct HttpRpc {
url: String, url: String,
@ -29,7 +28,7 @@ impl Clone for HttpRpc {
} }
#[async_trait] #[async_trait]
impl Rpc for HttpRpc { impl ExecutionRpc for HttpRpc {
fn new(rpc: &str) -> Result<Self> { fn new(rpc: &str) -> Result<Self> {
let http = Http::from_str(rpc)?; let http = Http::from_str(rpc)?;
let mut client = RetryClient::new(http, Box::new(HttpRateLimitRetryPolicy), 100, 250); let mut client = RetryClient::new(http, Box::new(HttpRateLimitRetryPolicy), 100, 250);
@ -47,7 +46,6 @@ impl Rpc for HttpRpc {
slots: &[H256], slots: &[H256],
block: u64, block: u64,
) -> Result<EIP1186ProofResponse> { ) -> Result<EIP1186ProofResponse> {
trace!("fetching proof");
let block = Some(BlockId::from(block)); let block = Some(BlockId::from(block));
let proof_response = self let proof_response = self
.provider .provider

View File

@ -10,7 +10,7 @@ use eyre::{eyre, Result};
use crate::types::CallOpts; use crate::types::CallOpts;
use super::Rpc; use super::ExecutionRpc;
#[derive(Clone)] #[derive(Clone)]
pub struct MockRpc { pub struct MockRpc {
@ -18,7 +18,7 @@ pub struct MockRpc {
} }
#[async_trait] #[async_trait]
impl Rpc for MockRpc { impl ExecutionRpc for MockRpc {
fn new(rpc: &str) -> Result<Self> { fn new(rpc: &str) -> Result<Self> {
let path = PathBuf::from(rpc); let path = PathBuf::from(rpc);
Ok(MockRpc { path }) Ok(MockRpc { path })

View File

@ -11,7 +11,7 @@ pub mod http_rpc;
pub mod mock_rpc; pub mod mock_rpc;
#[async_trait] #[async_trait]
pub trait Rpc: Send + Clone + 'static { pub trait ExecutionRpc: Send + Clone + 'static {
fn new(rpc: &str) -> Result<Self> fn new(rpc: &str) -> Result<Self>
where where
Self: Sized; Self: Sized;