lint
This commit is contained in:
parent
a441bde2c8
commit
170ca7c442
154
src/consensus.rs
154
src/consensus.rs
|
@ -1,10 +1,10 @@
|
||||||
use eyre::Result;
|
|
||||||
use ssz_rs::prelude::*;
|
|
||||||
use blst::min_pk::{PublicKey, Signature};
|
use blst::min_pk::{PublicKey, Signature};
|
||||||
use blst::BLST_ERROR;
|
use blst::BLST_ERROR;
|
||||||
|
use eyre::Result;
|
||||||
|
use ssz_rs::prelude::*;
|
||||||
|
|
||||||
use super::consensus_rpc::*;
|
use crate::consensus_rpc::*;
|
||||||
use super::utils::*;
|
use crate::utils::*;
|
||||||
|
|
||||||
pub struct ConsensusClient {
|
pub struct ConsensusClient {
|
||||||
consensus_rpc: ConsensusRpc,
|
consensus_rpc: ConsensusRpc,
|
||||||
|
@ -20,7 +20,6 @@ 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: &str) -> Result<ConsensusClient> {
|
||||||
|
|
||||||
let consensus_rpc = ConsensusRpc::new(nimbus_rpc);
|
let consensus_rpc = ConsensusRpc::new(nimbus_rpc);
|
||||||
|
|
||||||
let mut bootstrap = consensus_rpc.get_bootstrap(checkpoint_block_root).await?;
|
let mut bootstrap = consensus_rpc.get_bootstrap(checkpoint_block_root).await?;
|
||||||
|
@ -28,7 +27,7 @@ impl ConsensusClient {
|
||||||
let committee_valid = is_current_committee_proof_valid(
|
let committee_valid = is_current_committee_proof_valid(
|
||||||
&bootstrap.header,
|
&bootstrap.header,
|
||||||
&mut bootstrap.current_sync_committee,
|
&mut bootstrap.current_sync_committee,
|
||||||
&bootstrap.current_sync_committee_branch
|
&bootstrap.current_sync_committee_branch,
|
||||||
);
|
);
|
||||||
|
|
||||||
let header_hash = bootstrap.header.hash_tree_root()?;
|
let header_hash = bootstrap.header.hash_tree_root()?;
|
||||||
|
@ -44,7 +43,10 @@ impl ConsensusClient {
|
||||||
next_sync_committee: None,
|
next_sync_committee: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ConsensusClient { consensus_rpc, store })
|
Ok(ConsensusClient {
|
||||||
|
consensus_rpc,
|
||||||
|
store,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_execution_payload(&mut self) -> Result<ExecutionPayload> {
|
pub async fn get_execution_payload(&mut self) -> Result<ExecutionPayload> {
|
||||||
|
@ -52,7 +54,7 @@ impl ConsensusClient {
|
||||||
let mut block = self.consensus_rpc.get_block(slot).await?;
|
let mut block = self.consensus_rpc.get_block(slot).await?;
|
||||||
let block_hash = block.hash_tree_root()?;
|
let block_hash = block.hash_tree_root()?;
|
||||||
let verified_block_hash = self.store.header.hash_tree_root()?;
|
let verified_block_hash = self.store.header.hash_tree_root()?;
|
||||||
|
|
||||||
if verified_block_hash != block_hash {
|
if verified_block_hash != block_hash {
|
||||||
Err(eyre::eyre!("Block Root Mismatch"))
|
Err(eyre::eyre!("Block Root Mismatch"))
|
||||||
} else {
|
} else {
|
||||||
|
@ -61,7 +63,6 @@ impl ConsensusClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn sync(&mut self) -> Result<()> {
|
pub async fn sync(&mut self) -> Result<()> {
|
||||||
|
|
||||||
let current_period = calc_sync_period(self.store.header.slot);
|
let current_period = calc_sync_period(self.store.header.slot);
|
||||||
let updates = self.consensus_rpc.get_updates(current_period).await?;
|
let updates = self.consensus_rpc.get_updates(current_period).await?;
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ impl ConsensusClient {
|
||||||
self.apply_update(&finality_update_generic);
|
self.apply_update(&finality_update_generic);
|
||||||
|
|
||||||
println!("synced up to slot: {}", self.store.header.slot);
|
println!("synced up to slot: {}", self.store.header.slot);
|
||||||
|
|
||||||
self.consensus_rpc.get_block(self.store.header.slot).await?;
|
self.consensus_rpc.get_block(self.store.header.slot).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -94,7 +95,7 @@ impl ConsensusClient {
|
||||||
fn verify_update(&mut self, update: &mut Update) -> Result<()> {
|
fn verify_update(&mut self, update: &mut Update) -> Result<()> {
|
||||||
let current_slot = self.store.header.slot;
|
let current_slot = self.store.header.slot;
|
||||||
let update_slot = update.finalized_header.slot;
|
let update_slot = update.finalized_header.slot;
|
||||||
|
|
||||||
let current_period = calc_sync_period(current_slot);
|
let current_period = calc_sync_period(current_slot);
|
||||||
let update_period = calc_sync_period(update_slot);
|
let update_period = calc_sync_period(update_slot);
|
||||||
|
|
||||||
|
@ -102,14 +103,16 @@ impl ConsensusClient {
|
||||||
return Err(eyre::eyre!("Invalid Update"));
|
return Err(eyre::eyre!("Invalid Update"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(update.signature_slot > update.attested_header.slot && update.attested_header.slot > update.finalized_header.slot) {
|
if !(update.signature_slot > update.attested_header.slot
|
||||||
|
&& update.attested_header.slot > update.finalized_header.slot)
|
||||||
|
{
|
||||||
return Err(eyre::eyre!("Invalid Update"));
|
return Err(eyre::eyre!("Invalid Update"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let finality_branch_valid = is_finality_proof_valid(
|
let finality_branch_valid = is_finality_proof_valid(
|
||||||
&update.attested_header,
|
&update.attested_header,
|
||||||
&mut update.finalized_header,
|
&mut update.finalized_header,
|
||||||
&update.finality_branch
|
&update.finality_branch,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !(finality_branch_valid) {
|
if !(finality_branch_valid) {
|
||||||
|
@ -120,7 +123,7 @@ impl ConsensusClient {
|
||||||
let next_committee_branch_valid = is_next_committee_proof_valid(
|
let next_committee_branch_valid = is_next_committee_proof_valid(
|
||||||
&update.attested_header,
|
&update.attested_header,
|
||||||
&mut update.next_sync_committee.clone().unwrap(),
|
&mut update.next_sync_committee.clone().unwrap(),
|
||||||
&update.next_sync_committee_branch
|
&update.next_sync_committee_branch,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !next_committee_branch_valid {
|
if !next_committee_branch_valid {
|
||||||
|
@ -134,7 +137,8 @@ impl ConsensusClient {
|
||||||
self.store.next_sync_committee.as_ref().unwrap()
|
self.store.next_sync_committee.as_ref().unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
let pks = get_participating_keys(sync_committee, &update.sync_aggregate.sync_committee_bits)?;
|
let pks =
|
||||||
|
get_participating_keys(sync_committee, &update.sync_aggregate.sync_committee_bits)?;
|
||||||
let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect();
|
let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect();
|
||||||
|
|
||||||
let committee_quorum = pks.len() > 1;
|
let committee_quorum = pks.len() > 1;
|
||||||
|
@ -155,32 +159,37 @@ impl ConsensusClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_update(&mut self, update: &Update) {
|
fn apply_update(&mut self, update: &Update) {
|
||||||
|
|
||||||
let current_period = calc_sync_period(self.store.header.slot);
|
let current_period = calc_sync_period(self.store.header.slot);
|
||||||
let update_period = calc_sync_period(update.finalized_header.slot);
|
let update_period = calc_sync_period(update.finalized_header.slot);
|
||||||
|
|
||||||
self.store.header = update.finalized_header.clone();
|
self.store.header = update.finalized_header.clone();
|
||||||
|
|
||||||
if self.store.next_sync_committee.is_none() {
|
if self.store.next_sync_committee.is_none() {
|
||||||
self.store.next_sync_committee = Some(update.next_sync_committee.as_ref().unwrap().clone());
|
self.store.next_sync_committee =
|
||||||
|
Some(update.next_sync_committee.as_ref().unwrap().clone());
|
||||||
} else if update_period == current_period + 1 {
|
} else if update_period == current_period + 1 {
|
||||||
self.store.current_sync_committee = self.store.next_sync_committee.as_ref().unwrap().clone();
|
self.store.current_sync_committee =
|
||||||
self.store.next_sync_committee = Some(update.next_sync_committee.as_ref().unwrap().clone());
|
self.store.next_sync_committee.as_ref().unwrap().clone();
|
||||||
|
self.store.next_sync_committee =
|
||||||
|
Some(update.next_sync_committee.as_ref().unwrap().clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_participating_keys(committee: &SyncCommittee, bitfield: &Bitvector<512>) -> Result<Vec<PublicKey>> {
|
fn get_participating_keys(
|
||||||
let mut pks: Vec<PublicKey> = Vec::new();
|
committee: &SyncCommittee,
|
||||||
bitfield.iter().enumerate().for_each(|(i, bit)| {
|
bitfield: &Bitvector<512>,
|
||||||
if bit == true {
|
) -> Result<Vec<PublicKey>> {
|
||||||
let pk = &committee.pubkeys[i];
|
let mut pks: Vec<PublicKey> = Vec::new();
|
||||||
let pk = PublicKey::from_bytes(&pk).unwrap();
|
bitfield.iter().enumerate().for_each(|(i, bit)| {
|
||||||
pks.push(pk);
|
if bit == true {
|
||||||
}
|
let pk = &committee.pubkeys[i];
|
||||||
});
|
let pk = PublicKey::from_bytes(&pk).unwrap();
|
||||||
|
pks.push(pk);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(pks)
|
Ok(pks)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_aggregate_valid(sig_bytes: &SignatureBytes, msg: &[u8], pks: &[&PublicKey]) -> bool {
|
fn is_aggregate_valid(sig_bytes: &SignatureBytes, msg: &[u8], pks: &[&PublicKey]) -> bool {
|
||||||
|
@ -192,35 +201,39 @@ fn is_aggregate_valid(sig_bytes: &SignatureBytes, msg: &[u8], pks: &[&PublicKey]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_finality_proof_valid(attested_header: &Header, finality_header: &mut Header, finality_branch: &Vec<Bytes32>) -> bool {
|
fn is_finality_proof_valid(
|
||||||
let finality_header_hash_res = finality_header.hash_tree_root();
|
attested_header: &Header,
|
||||||
if finality_header_hash_res.is_err() {
|
finality_header: &mut Header,
|
||||||
return false;
|
finality_branch: &Vec<Bytes32>,
|
||||||
}
|
) -> bool {
|
||||||
|
let finality_header_hash_res = finality_header.hash_tree_root();
|
||||||
|
if finality_header_hash_res.is_err() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
|
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
|
||||||
if attested_header_state_root_res.is_err() {
|
if attested_header_state_root_res.is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let finality_branch_res = branch_to_nodes(finality_branch.clone());
|
let finality_branch_res = branch_to_nodes(finality_branch.clone());
|
||||||
if finality_branch_res.is_err() {
|
if finality_branch_res.is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_valid_merkle_branch(
|
is_valid_merkle_branch(
|
||||||
&finality_header_hash_res.unwrap(),
|
&finality_header_hash_res.unwrap(),
|
||||||
finality_branch_res.unwrap().iter(),
|
finality_branch_res.unwrap().iter(),
|
||||||
6,
|
6,
|
||||||
41,
|
41,
|
||||||
&attested_header_state_root_res.unwrap()
|
&attested_header_state_root_res.unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_next_committee_proof_valid(
|
fn is_next_committee_proof_valid(
|
||||||
attested_header: &Header,
|
attested_header: &Header,
|
||||||
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();
|
let next_committee_hash_res = next_committee.hash_tree_root();
|
||||||
if next_committee_hash_res.is_err() {
|
if next_committee_hash_res.is_err() {
|
||||||
|
@ -242,14 +255,14 @@ fn is_next_committee_proof_valid(
|
||||||
next_committee_branch_res.unwrap().iter(),
|
next_committee_branch_res.unwrap().iter(),
|
||||||
5,
|
5,
|
||||||
23,
|
23,
|
||||||
&attested_header_state_root_res.unwrap()
|
&attested_header_state_root_res.unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_current_committee_proof_valid(
|
fn is_current_committee_proof_valid(
|
||||||
attested_header: &Header,
|
attested_header: &Header,
|
||||||
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();
|
let next_committee_hash_res = current_committee.hash_tree_root();
|
||||||
if next_committee_hash_res.is_err() {
|
if next_committee_hash_res.is_err() {
|
||||||
|
@ -271,7 +284,7 @@ fn is_current_committee_proof_valid(
|
||||||
next_committee_branch_res.unwrap().iter(),
|
next_committee_branch_res.unwrap().iter(),
|
||||||
5,
|
5,
|
||||||
22,
|
22,
|
||||||
&attested_header_state_root_res.unwrap()
|
&attested_header_state_root_res.unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,11 +294,18 @@ fn calc_sync_period(slot: u64) -> u64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
|
fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
|
||||||
branch.iter().map(|elem| bytes32_to_node(elem)).collect::<Result<Vec<Node>>>()
|
branch
|
||||||
|
.iter()
|
||||||
|
.map(|elem| bytes32_to_node(elem))
|
||||||
|
.collect::<Result<Vec<Node>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
|
fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
|
||||||
let genesis_root = hex::decode("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb")?.to_vec().try_into().unwrap();
|
let genesis_root =
|
||||||
|
hex::decode("043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb")?
|
||||||
|
.to_vec()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
let domain_type = &hex::decode("07000000")?[..];
|
let domain_type = &hex::decode("07000000")?[..];
|
||||||
let fork_version = Vector::from_iter(hex::decode("02001020").unwrap());
|
let fork_version = Vector::from_iter(hex::decode("02001020").unwrap());
|
||||||
let domain = compute_domain(domain_type, fork_version, genesis_root)?;
|
let domain = compute_domain(domain_type, fork_version, genesis_root)?;
|
||||||
|
@ -295,7 +315,7 @@ fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
|
||||||
#[derive(SimpleSerialize, Default, Debug)]
|
#[derive(SimpleSerialize, Default, Debug)]
|
||||||
struct SigningData {
|
struct SigningData {
|
||||||
object_root: Bytes32,
|
object_root: Bytes32,
|
||||||
domain: Bytes32
|
domain: Bytes32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(SimpleSerialize, Default, Debug)]
|
#[derive(SimpleSerialize, Default, Debug)]
|
||||||
|
@ -305,11 +325,18 @@ struct ForkData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result<Node> {
|
fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result<Node> {
|
||||||
let mut data = SigningData { object_root, domain };
|
let mut data = SigningData {
|
||||||
|
object_root,
|
||||||
|
domain,
|
||||||
|
};
|
||||||
Ok(data.hash_tree_root()?)
|
Ok(data.hash_tree_root()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_domain(domain_type: &[u8], fork_version: Vector<u8, 4>, genesis_root: Bytes32) -> Result<Bytes32> {
|
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 fork_data_root = compute_fork_data_root(fork_version, genesis_root)?;
|
||||||
let start = domain_type;
|
let start = domain_type;
|
||||||
let end = &fork_data_root.as_bytes()[..28];
|
let end = &fork_data_root.as_bytes()[..28];
|
||||||
|
@ -317,9 +344,14 @@ fn compute_domain(domain_type: &[u8], fork_version: Vector<u8, 4>, genesis_root:
|
||||||
Ok(d.to_vec().try_into().unwrap())
|
Ok(d.to_vec().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_fork_data_root(current_version: Vector<u8, 4>, genesis_validator_root: Bytes32) -> Result<Node> {
|
fn compute_fork_data_root(
|
||||||
|
current_version: Vector<u8, 4>,
|
||||||
|
genesis_validator_root: Bytes32,
|
||||||
|
) -> Result<Node> {
|
||||||
let current_version = current_version.try_into()?;
|
let current_version = current_version.try_into()?;
|
||||||
let mut fork_data = ForkData { current_version, genesis_validator_root };
|
let mut fork_data = ForkData {
|
||||||
|
current_version,
|
||||||
|
genesis_validator_root,
|
||||||
|
};
|
||||||
Ok(fork_data.hash_tree_root()?)
|
Ok(fork_data.hash_tree_root()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use ssz_rs::prelude::*;
|
|
||||||
use super::utils::*;
|
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
|
use ssz_rs::prelude::*;
|
||||||
|
|
||||||
|
use crate::utils::*;
|
||||||
|
|
||||||
pub struct ConsensusRpc {
|
pub struct ConsensusRpc {
|
||||||
rpc: String,
|
rpc: String,
|
||||||
|
@ -9,30 +10,44 @@ pub struct ConsensusRpc {
|
||||||
|
|
||||||
impl ConsensusRpc {
|
impl ConsensusRpc {
|
||||||
pub fn new(rpc: &str) -> Self {
|
pub fn new(rpc: &str) -> Self {
|
||||||
ConsensusRpc { rpc: rpc.to_string() }
|
ConsensusRpc {
|
||||||
|
rpc: rpc.to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_bootstrap(&self, block_root: &str) -> Result<Bootstrap> {
|
pub async fn get_bootstrap(&self, block_root: &str) -> Result<Bootstrap> {
|
||||||
let req = format!("{}/eth/v0/beacon/light_client/bootstrap/{}", self.rpc, block_root);
|
let req = format!(
|
||||||
|
"{}/eth/v0/beacon/light_client/bootstrap/{}",
|
||||||
|
self.rpc, block_root
|
||||||
|
);
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_updates(&self, period: u64) -> Result<Vec<Update>> {
|
pub async fn get_updates(&self, period: u64) -> Result<Vec<Update>> {
|
||||||
let req = format!("{}/eth/v0/beacon/light_client/updates?start_period={}&count=1000", self.rpc, period);
|
let req = format!(
|
||||||
|
"{}/eth/v0/beacon/light_client/updates?start_period={}&count=1000",
|
||||||
|
self.rpc, period
|
||||||
|
);
|
||||||
let res = reqwest::get(req).await?.json::<UpdateResponse>().await?;
|
let res = reqwest::get(req).await?.json::<UpdateResponse>().await?;
|
||||||
Ok(res.data)
|
Ok(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_finality_update(&self) -> Result<FinalityUpdate> {
|
pub async fn get_finality_update(&self) -> Result<FinalityUpdate> {
|
||||||
let req = format!("{}/eth/v0/beacon/light_client/finality_update", self.rpc);
|
let req = format!("{}/eth/v0/beacon/light_client/finality_update", self.rpc);
|
||||||
let res = reqwest::get(req).await?.json::<FinalityUpdateResponse>().await?;
|
let res = reqwest::get(req)
|
||||||
|
.await?
|
||||||
|
.json::<FinalityUpdateResponse>()
|
||||||
|
.await?;
|
||||||
Ok(res.data)
|
Ok(res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_block(&self, slot: u64) -> Result<BeaconBlock>{
|
pub async fn get_block(&self, slot: u64) -> Result<BeaconBlock> {
|
||||||
let req = format!("{}/eth/v2/beacon/blocks/{}", self.rpc, slot);
|
let req = format!("{}/eth/v2/beacon/blocks/{}", self.rpc, slot);
|
||||||
let res = reqwest::get(req).await?.json::<BeaconBlockResponse>().await?;
|
let res = reqwest::get(req)
|
||||||
|
.await?
|
||||||
|
.json::<BeaconBlockResponse>()
|
||||||
|
.await?;
|
||||||
Ok(res.data.message)
|
Ok(res.data.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,7 +114,7 @@ pub struct ExecutionPayload {
|
||||||
gas_used: u64,
|
gas_used: u64,
|
||||||
#[serde(deserialize_with = "u64_deserialize")]
|
#[serde(deserialize_with = "u64_deserialize")]
|
||||||
timestamp: u64,
|
timestamp: u64,
|
||||||
#[serde(deserialize_with ="extra_data_deserialize")]
|
#[serde(deserialize_with = "extra_data_deserialize")]
|
||||||
extra_data: List<u8, 32>,
|
extra_data: List<u8, 32>,
|
||||||
#[serde(deserialize_with = "u256_deserialize")]
|
#[serde(deserialize_with = "u256_deserialize")]
|
||||||
base_fee_per_gas: U256,
|
base_fee_per_gas: U256,
|
||||||
|
@ -147,7 +162,7 @@ pub struct Eth1Data {
|
||||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||||
deposit_root: Bytes32,
|
deposit_root: Bytes32,
|
||||||
#[serde(deserialize_with = "u64_deserialize")]
|
#[serde(deserialize_with = "u64_deserialize")]
|
||||||
deposit_count: u64,
|
deposit_count: u64,
|
||||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||||
block_hash: Bytes32,
|
block_hash: Bytes32,
|
||||||
}
|
}
|
||||||
|
@ -244,76 +259,120 @@ struct BootstrapData {
|
||||||
v: Bootstrap,
|
v: Bootstrap,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pubkey_deserialize<'de, D>(deserializer: D) -> Result<BLSPubKey, D::Error> where D: serde::Deserializer<'de> {
|
fn pubkey_deserialize<'de, D>(deserializer: D) -> Result<BLSPubKey, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let key: String = serde::Deserialize::deserialize(deserializer)?;
|
let key: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let key_bytes = hex_str_to_bytes(&key).map_err(D::Error::custom)?;
|
let key_bytes = hex_str_to_bytes(&key).map_err(D::Error::custom)?;
|
||||||
Ok(Vector::from_iter(key_bytes))
|
Ok(Vector::from_iter(key_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pubkeys_deserialize<'de, D>(deserializer: D) -> Result<Vector<BLSPubKey, 512>, D::Error> where D: serde::Deserializer<'de> {
|
fn pubkeys_deserialize<'de, D>(deserializer: D) -> Result<Vector<BLSPubKey, 512>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let keys: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
let keys: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||||
Ok(keys.iter().map(|key| {
|
Ok(keys
|
||||||
let key_bytes = hex_str_to_bytes(key)?;
|
.iter()
|
||||||
Ok(Vector::from_iter(key_bytes))
|
.map(|key| {
|
||||||
}).collect::<Result<Vector<BLSPubKey, 512>>>().map_err(D::Error::custom)?)
|
let key_bytes = hex_str_to_bytes(key)?;
|
||||||
|
Ok(Vector::from_iter(key_bytes))
|
||||||
|
})
|
||||||
|
.collect::<Result<Vector<BLSPubKey, 512>>>()
|
||||||
|
.map_err(D::Error::custom)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature_deserialize<'de, D>(deserializer: D) -> Result<SignatureBytes, D::Error> where D: serde::Deserializer<'de> {
|
fn signature_deserialize<'de, D>(deserializer: D) -> Result<SignatureBytes, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let sig: String = serde::Deserialize::deserialize(deserializer)?;
|
let sig: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let sig_bytes = hex_str_to_bytes(&sig).map_err(D::Error::custom)?;
|
let sig_bytes = hex_str_to_bytes(&sig).map_err(D::Error::custom)?;
|
||||||
Ok(Vector::from_iter(sig_bytes))
|
Ok(Vector::from_iter(sig_bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branch_deserialize<'de, D>(deserializer: D) -> Result<Vec<Bytes32>, D::Error> where D: serde::Deserializer<'de> {
|
fn branch_deserialize<'de, D>(deserializer: D) -> Result<Vec<Bytes32>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||||
Ok(branch.iter().map(|elem| {
|
Ok(branch
|
||||||
let elem_bytes = hex_str_to_bytes(elem)?;
|
.iter()
|
||||||
Ok(Vector::from_iter(elem_bytes))
|
.map(|elem| {
|
||||||
}).collect::<Result<_>>().map_err(D::Error::custom)?)
|
let elem_bytes = hex_str_to_bytes(elem)?;
|
||||||
|
Ok(Vector::from_iter(elem_bytes))
|
||||||
|
})
|
||||||
|
.collect::<Result<_>>()
|
||||||
|
.map_err(D::Error::custom)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn u64_deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error> where D: serde::Deserializer<'de> {
|
fn u64_deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
Ok(val.parse().unwrap())
|
Ok(val.parse().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn u256_deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error> where D: serde::Deserializer<'de> {
|
fn u256_deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
// TODO: support larger values
|
// TODO: support larger values
|
||||||
let i = val.parse::<u64>().map_err(D::Error::custom)?;
|
let i = val.parse::<u64>().map_err(D::Error::custom)?;
|
||||||
Ok(U256::from(i))
|
Ok(U256::from(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes32_deserialize<'de, D>(deserializer: D) -> Result<Bytes32, D::Error> where D: serde::Deserializer<'de> {
|
fn bytes32_deserialize<'de, D>(deserializer: D) -> Result<Bytes32, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||||
Ok(bytes.to_vec().try_into().unwrap())
|
Ok(bytes.to_vec().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn logs_bloom_deserialize<'de, D>(deserializer: D) -> Result<LogsBloom, D::Error> where D: serde::Deserializer<'de> {
|
fn logs_bloom_deserialize<'de, D>(deserializer: D) -> Result<LogsBloom, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||||
Ok(bytes.to_vec().try_into().unwrap())
|
Ok(bytes.to_vec().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address_deserialize<'de, D>(deserializer: D) -> Result<Address, D::Error> where D: serde::Deserializer<'de> {
|
fn address_deserialize<'de, D>(deserializer: D) -> Result<Address, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||||
Ok(bytes.to_vec().try_into().unwrap())
|
Ok(bytes.to_vec().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extra_data_deserialize<'de, D>(deserializer: D) -> Result<List<u8, 32>, D::Error> where D: serde::Deserializer<'de> {
|
fn extra_data_deserialize<'de, D>(deserializer: D) -> Result<List<u8, 32>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||||
Ok(bytes.to_vec().try_into().unwrap())
|
Ok(bytes.to_vec().try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transactions_deserialize<'de, D>(deserializer: D) -> Result<List<Transaction, 1048576>, D::Error> where D: serde::Deserializer<'de> {
|
fn transactions_deserialize<'de, D>(deserializer: D) -> Result<List<Transaction, 1048576>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let transactions: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
let transactions: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||||
let transactions = transactions.iter().map(|tx| {
|
let transactions = transactions
|
||||||
let tx = hex_str_to_bytes(tx).unwrap();
|
.iter()
|
||||||
let tx: Transaction = List::from_iter(tx);
|
.map(|tx| {
|
||||||
tx
|
let tx = hex_str_to_bytes(tx).unwrap();
|
||||||
}).collect::<List<Transaction,1048576>>();
|
let tx: Transaction = List::from_iter(tx);
|
||||||
|
tx
|
||||||
|
})
|
||||||
|
.collect::<List<Transaction, 1048576>>();
|
||||||
Ok(transactions)
|
Ok(transactions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
use ethers::prelude::{Address, U256};
|
||||||
use ethers::utils::keccak256;
|
use ethers::utils::keccak256;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use ethers::prelude::{U256, Address};
|
|
||||||
|
|
||||||
use crate::proof::{encode_account, verify_proof};
|
|
||||||
use crate::consensus_rpc::ExecutionPayload;
|
use crate::consensus_rpc::ExecutionPayload;
|
||||||
use crate::execution_rpc::ExecutionRpc;
|
use crate::execution_rpc::ExecutionRpc;
|
||||||
|
use crate::proof::{encode_account, verify_proof};
|
||||||
|
|
||||||
pub struct ExecutionClient {
|
pub struct ExecutionClient {
|
||||||
execution_rpc: ExecutionRpc
|
execution_rpc: ExecutionRpc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecutionClient {
|
impl ExecutionClient {
|
||||||
|
@ -17,12 +17,20 @@ impl ExecutionClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_balance(&self, account: &Address, payload: &ExecutionPayload) -> Result<U256> {
|
pub async fn get_balance(&self, account: &Address, payload: &ExecutionPayload) -> Result<U256> {
|
||||||
let proof = self.execution_rpc.get_proof(&account, payload.block_number).await?;
|
let proof = self
|
||||||
|
.execution_rpc
|
||||||
|
.get_proof(&account, payload.block_number)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let account_path = keccak256(account.as_bytes()).to_vec();
|
let account_path = keccak256(account.as_bytes()).to_vec();
|
||||||
let account_encoded = encode_account(&proof);
|
let account_encoded = encode_account(&proof);
|
||||||
|
|
||||||
let is_valid = verify_proof(&proof.account_proof, &payload.state_root, &account_path, &account_encoded);
|
let is_valid = verify_proof(
|
||||||
|
&proof.account_proof,
|
||||||
|
&payload.state_root,
|
||||||
|
&account_path,
|
||||||
|
&account_encoded,
|
||||||
|
);
|
||||||
|
|
||||||
if !is_valid {
|
if !is_valid {
|
||||||
eyre::bail!("Invalid Proof");
|
eyre::bail!("Invalid Proof");
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use ethers::prelude::{Address, U256, H256};
|
use ethers::prelude::{Address, H256, U256};
|
||||||
use jsonrpsee::{http_client::HttpClientBuilder, rpc_params, core::client::ClientT};
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use serde::Deserialize;
|
use jsonrpsee::{core::client::ClientT, http_client::HttpClientBuilder, rpc_params};
|
||||||
use serde::de::Error;
|
use serde::de::Error;
|
||||||
use super::utils::*;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::utils::hex_str_to_bytes;
|
||||||
|
|
||||||
pub struct ExecutionRpc {
|
pub struct ExecutionRpc {
|
||||||
rpc: String,
|
rpc: String,
|
||||||
|
@ -11,7 +12,9 @@ pub struct ExecutionRpc {
|
||||||
|
|
||||||
impl ExecutionRpc {
|
impl ExecutionRpc {
|
||||||
pub fn new(rpc: &str) -> Self {
|
pub fn new(rpc: &str) -> Self {
|
||||||
ExecutionRpc { rpc: rpc.to_string() }
|
ExecutionRpc {
|
||||||
|
rpc: rpc.to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_proof(&self, address: &Address, block: u64) -> Result<Proof> {
|
pub async fn get_proof(&self, address: &Address, block: u64) -> Result<Proof> {
|
||||||
|
@ -35,10 +38,14 @@ pub struct Proof {
|
||||||
pub account_proof: Vec<Vec<u8>>,
|
pub account_proof: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn proof_deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error> where D: serde::Deserializer<'de> {
|
fn proof_deserialize<'de, D>(deserializer: D) -> Result<Vec<Vec<u8>>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||||
Ok(branch.iter().map(|elem| {
|
Ok(branch
|
||||||
hex_str_to_bytes(elem)
|
.iter()
|
||||||
}).collect::<Result<_>>().map_err(D::Error::custom)?)
|
.map(|elem| hex_str_to_bytes(elem))
|
||||||
|
.collect::<Result<_>>()
|
||||||
|
.map_err(D::Error::custom)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -10,23 +10,25 @@ pub mod consensus;
|
||||||
pub mod consensus_rpc;
|
pub mod consensus_rpc;
|
||||||
pub mod execution;
|
pub mod execution;
|
||||||
pub mod execution_rpc;
|
pub mod execution_rpc;
|
||||||
pub mod utils;
|
|
||||||
pub mod proof;
|
pub mod proof;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
||||||
let rpc = "http://testing.prater.beacon-api.nimbus.team";
|
let rpc = "http://testing.prater.beacon-api.nimbus.team";
|
||||||
let checkpoint = "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99";
|
let checkpoint = "0x172128eadf1da46467f4d6a822206698e2d3f957af117dd650954780d680dc99";
|
||||||
let mut client = ConsensusClient::new(rpc, checkpoint).await?;
|
let mut client = ConsensusClient::new(rpc, checkpoint).await?;
|
||||||
|
|
||||||
let rpc = "https://eth-goerli.g.alchemy.com:443/v2/o_8Qa9kgwDPf9G8sroyQ-uQtyhyWa3ao";
|
let rpc = "https://eth-goerli.g.alchemy.com:443/v2/o_8Qa9kgwDPf9G8sroyQ-uQtyhyWa3ao";
|
||||||
let execution = ExecutionClient::new(rpc);
|
let execution = ExecutionClient::new(rpc);
|
||||||
|
|
||||||
client.sync().await?;
|
client.sync().await?;
|
||||||
|
|
||||||
let payload = client.get_execution_payload().await?;
|
let payload = client.get_execution_payload().await?;
|
||||||
println!("verified execution block hash: {}", hex::encode(&payload.block_hash));
|
println!(
|
||||||
|
"verified execution block hash: {}",
|
||||||
|
hex::encode(&payload.block_hash)
|
||||||
|
);
|
||||||
|
|
||||||
let addr = Address::from_str("0x25c4a76E7d118705e7Ea2e9b7d8C59930d8aCD3b")?;
|
let addr = Address::from_str("0x25c4a76E7d118705e7Ea2e9b7d8C59930d8aCD3b")?;
|
||||||
let balance = execution.get_balance(&addr, &payload).await?;
|
let balance = execution.get_balance(&addr, &payload).await?;
|
||||||
|
@ -34,4 +36,3 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
src/proof.rs
11
src/proof.rs
|
@ -1,9 +1,9 @@
|
||||||
use ethers::utils::keccak256;
|
use ethers::utils::keccak256;
|
||||||
use ethers::utils::rlp::{RlpStream, decode_list};
|
use ethers::utils::rlp::{decode_list, RlpStream};
|
||||||
use super::execution_rpc::Proof;
|
|
||||||
|
use crate::execution_rpc::Proof;
|
||||||
|
|
||||||
pub fn verify_proof(proof: &Vec<Vec<u8>>, root: &Vec<u8>, path: &Vec<u8>, value: &Vec<u8>) -> bool {
|
pub fn verify_proof(proof: &Vec<Vec<u8>>, root: &Vec<u8>, path: &Vec<u8>, value: &Vec<u8>) -> bool {
|
||||||
|
|
||||||
let mut expected_hash = root.clone();
|
let mut expected_hash = root.clone();
|
||||||
let mut path_offset = 0;
|
let mut path_offset = 0;
|
||||||
|
|
||||||
|
@ -13,14 +13,12 @@ pub fn verify_proof(proof: &Vec<Vec<u8>>, root: &Vec<u8>, path: &Vec<u8>, value:
|
||||||
}
|
}
|
||||||
|
|
||||||
let node_list: Vec<Vec<u8>> = decode_list(node);
|
let node_list: Vec<Vec<u8>> = decode_list(node);
|
||||||
|
|
||||||
if node_list.len() == 17 {
|
|
||||||
|
|
||||||
|
if node_list.len() == 17 {
|
||||||
let nibble = get_nibble(&path, path_offset);
|
let nibble = get_nibble(&path, path_offset);
|
||||||
expected_hash = node_list[nibble as usize].clone();
|
expected_hash = node_list[nibble as usize].clone();
|
||||||
|
|
||||||
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 {
|
||||||
if &node_list[1] != value {
|
if &node_list[1] != value {
|
||||||
|
@ -59,4 +57,3 @@ pub fn encode_account(proof: &Proof) -> Vec<u8> {
|
||||||
pub fn get_account_path(addr: &Vec<u8>) -> Vec<u8> {
|
pub fn get_account_path(addr: &Vec<u8>) -> Vec<u8> {
|
||||||
keccak256(addr).to_vec()
|
keccak256(addr).to_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use ssz_rs::{Node, Vector};
|
use ssz_rs::{Node, Vector};
|
||||||
use super::consensus_rpc::Bytes32;
|
|
||||||
|
use crate::consensus_rpc::Bytes32;
|
||||||
|
|
||||||
pub fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>> {
|
pub fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>> {
|
||||||
let stripped = s.strip_prefix("0x").unwrap_or(s);
|
let stripped = s.strip_prefix("0x").unwrap_or(s);
|
||||||
|
|
Loading…
Reference in New Issue