This commit is contained in:
Noah Citron 2022-08-20 16:33:32 -04:00
parent a441bde2c8
commit 170ca7c442
7 changed files with 230 additions and 125 deletions

View File

@ -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> {
@ -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?;
@ -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,22 +159,27 @@ 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(
committee: &SyncCommittee,
bitfield: &Bitvector<512>,
) -> Result<Vec<PublicKey>> {
let mut pks: Vec<PublicKey> = Vec::new(); let mut pks: Vec<PublicKey> = Vec::new();
bitfield.iter().enumerate().for_each(|(i, bit)| { bitfield.iter().enumerate().for_each(|(i, bit)| {
if bit == true { if bit == true {
@ -192,7 +201,11 @@ 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(
attested_header: &Header,
finality_header: &mut Header,
finality_branch: &Vec<Bytes32>,
) -> bool {
let finality_header_hash_res = finality_header.hash_tree_root(); let finality_header_hash_res = finality_header.hash_tree_root();
if finality_header_hash_res.is_err() { if finality_header_hash_res.is_err() {
return false; return false;
@ -213,14 +226,14 @@ fn is_finality_proof_valid(attested_header: &Header, finality_header: &mut Heade
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()?)
} }

View File

@ -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)
} }
} }
@ -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
.iter()
.map(|key| {
let key_bytes = hex_str_to_bytes(key)?; let key_bytes = hex_str_to_bytes(key)?;
Ok(Vector::from_iter(key_bytes)) Ok(Vector::from_iter(key_bytes))
}).collect::<Result<Vector<BLSPubKey, 512>>>().map_err(D::Error::custom)?) })
.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
.iter()
.map(|elem| {
let elem_bytes = hex_str_to_bytes(elem)?; let elem_bytes = hex_str_to_bytes(elem)?;
Ok(Vector::from_iter(elem_bytes)) Ok(Vector::from_iter(elem_bytes))
}).collect::<Result<_>>().map_err(D::Error::custom)?) })
.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
.iter()
.map(|tx| {
let tx = hex_str_to_bytes(tx).unwrap(); let tx = hex_str_to_bytes(tx).unwrap();
let tx: Transaction = List::from_iter(tx); let tx: Transaction = List::from_iter(tx);
tx tx
}).collect::<List<Transaction,1048576>>(); })
.collect::<List<Transaction, 1048576>>();
Ok(transactions) Ok(transactions)
} }

View File

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

View File

@ -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)?)
} }

View File

@ -10,12 +10,11 @@ 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?;
@ -26,7 +25,10 @@ async fn main() -> Result<()> {
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(())
} }

View File

@ -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;
@ -15,12 +15,10 @@ 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()
} }

View File

@ -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);