From 56385f15ed41c651f99ec4ccf355652b481acb02 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Mon, 12 Sep 2022 19:23:37 -0400 Subject: [PATCH] feat: support finalized block tag (#28) --- client/src/client.rs | 12 ++-- client/src/node.rs | 113 +++++++++++++++++++++---------------- client/src/rpc.rs | 11 ++-- consensus/src/consensus.rs | 48 +++++++++------- 4 files changed, 106 insertions(+), 78 deletions(-) diff --git a/client/src/client.rs b/client/src/client.rs index fe0cdbd..0b0f923 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -13,7 +13,7 @@ use tokio::spawn; use tokio::sync::Mutex; use tokio::time::sleep; -use crate::node::Node; +use crate::node::{BlockTag, Node}; use crate::rpc::Rpc; pub struct Client { @@ -55,7 +55,7 @@ impl Client { Ok(()) } - pub async fn call(&self, opts: &CallOpts, block: &Option) -> Result> { + pub async fn call(&self, opts: &CallOpts, block: &BlockTag) -> Result> { self.node.lock().await.call(opts, block) } @@ -63,15 +63,15 @@ impl Client { self.node.lock().await.estimate_gas(opts) } - pub async fn get_balance(&self, address: &Address, block: &Option) -> Result { + pub async fn get_balance(&self, address: &Address, block: &BlockTag) -> Result { self.node.lock().await.get_balance(address, block).await } - pub async fn get_nonce(&self, address: &Address, block: &Option) -> Result { + pub async fn get_nonce(&self, address: &Address, block: &BlockTag) -> Result { self.node.lock().await.get_nonce(address, block).await } - pub async fn get_code(&self, address: &Address, block: &Option) -> Result> { + pub async fn get_code(&self, address: &Address, block: &BlockTag) -> Result> { self.node.lock().await.get_code(address, block).await } @@ -114,7 +114,7 @@ impl Client { self.node.lock().await.get_block_number() } - pub async fn get_block_by_number(&self, block: &Option) -> Result { + pub async fn get_block_by_number(&self, block: &BlockTag) -> Result { self.node.lock().await.get_block_by_number(block) } diff --git a/client/src/node.rs b/client/src/node.rs index d25a3fe..488a6d5 100644 --- a/client/src/node.rs +++ b/client/src/node.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use ethers::prelude::{Address, U256}; use ethers::types::{Transaction, TransactionReceipt, H256}; -use eyre::Result; +use eyre::{eyre, Result}; use config::Config; use consensus::rpc::nimbus_rpc::NimbusRpc; @@ -20,7 +20,8 @@ pub struct Node { config: Arc, payloads: HashMap, block_hashes: HashMap, u64>, - block_head: u64, + latest_block: u64, + finalized_block: u64, } impl Node { @@ -42,75 +43,84 @@ impl Node { config, payloads, block_hashes, - block_head: 0, + latest_block: 0, + finalized_block: 0, }) } pub async fn sync(&mut self) -> Result<()> { self.consensus.sync().await?; - - let head = self.consensus.get_header(); - let payload = self - .consensus - .get_execution_payload(&Some(head.slot)) - .await?; - - self.block_head = payload.block_number; - self.block_hashes - .insert(payload.block_hash.to_vec(), payload.block_number); - self.payloads.insert(payload.block_number, payload); - - Ok(()) + self.update_payloads().await } pub async fn advance(&mut self) -> Result<()> { self.consensus.advance().await?; + self.update_payloads().await + } - let head = self.consensus.get_header(); - let payload = self + async fn update_payloads(&mut self) -> Result<()> { + let latest_header = self.consensus.get_header(); + let latest_payload = self .consensus - .get_execution_payload(&Some(head.slot)) + .get_execution_payload(&Some(latest_header.slot)) .await?; - self.block_head = payload.block_number; - self.block_hashes - .insert(payload.block_hash.to_vec(), payload.block_number); - self.payloads.insert(payload.block_number, payload); + self.latest_block = latest_payload.block_number; + self.block_hashes.insert( + latest_payload.block_hash.to_vec(), + latest_payload.block_number, + ); + self.payloads + .insert(latest_payload.block_number, latest_payload); + + let finalized_header = self.consensus.get_finalized_header(); + let finalized_payload = self + .consensus + .get_execution_payload(&Some(finalized_header.slot)) + .await?; + + self.finalized_block = finalized_payload.block_number; + self.block_hashes.insert( + finalized_payload.block_hash.to_vec(), + finalized_payload.block_number, + ); + self.payloads + .insert(finalized_payload.block_number, finalized_payload); Ok(()) } - pub fn call(&self, opts: &CallOpts, block: &Option) -> Result> { + pub fn call(&self, opts: &CallOpts, block: &BlockTag) -> Result> { let payload = self.get_payload(block)?; let mut evm = Evm::new(self.execution.clone(), payload, self.chain_id()); evm.call(opts) } pub fn estimate_gas(&self, opts: &CallOpts) -> Result { - let payload = self.get_payload(&None)?; + let payload = self.get_payload(&BlockTag::Latest)?; let mut evm = Evm::new(self.execution.clone(), payload, self.chain_id()); evm.estimate_gas(opts) } - pub async fn get_balance(&self, address: &Address, block: &Option) -> Result { + pub async fn get_balance(&self, address: &Address, block: &BlockTag) -> Result { let payload = self.get_payload(block)?; let account = self.execution.get_account(&address, None, &payload).await?; Ok(account.balance) } - pub async fn get_nonce(&self, address: &Address, block: &Option) -> Result { + pub async fn get_nonce(&self, address: &Address, block: &BlockTag) -> Result { let payload = self.get_payload(block)?; let account = self.execution.get_account(&address, None, &payload).await?; Ok(account.nonce) } - pub async fn get_code(&self, address: &Address, block: &Option) -> Result> { + pub async fn get_code(&self, address: &Address, block: &BlockTag) -> Result> { let payload = self.get_payload(block)?; self.execution.get_code(&address, &payload).await } pub async fn get_storage_at(&self, address: &Address, slot: H256) -> Result { - let payload = self.get_payload(&None)?; + let payload = self.get_payload(&BlockTag::Latest)?; let account = self .execution .get_account(address, Some(&[slot]), &payload) @@ -118,7 +128,7 @@ impl Node { let value = account.slots.get(&slot); match value { Some(value) => Ok(*value), - None => Err(eyre::eyre!("Slot Not Found")), + None => Err(eyre!("Slot Not Found")), } } @@ -142,7 +152,7 @@ impl Node { } pub fn get_gas_price(&self) -> Result { - let payload = self.get_payload(&None)?; + let payload = self.get_payload(&BlockTag::Latest)?; let base_fee = U256::from_little_endian(&payload.base_fee_per_gas.to_bytes_le()); let tip = U256::from(10_u64.pow(9)); Ok(base_fee + tip) @@ -154,18 +164,21 @@ impl Node { } pub fn get_block_number(&self) -> Result { - let payload = self.get_payload(&None)?; + let payload = self.get_payload(&BlockTag::Latest)?; Ok(payload.block_number) } - pub fn get_block_by_number(&self, block: &Option) -> Result { + pub fn get_block_by_number(&self, block: &BlockTag) -> Result { let payload = self.get_payload(block)?; self.execution.get_block(&payload) } pub fn get_block_by_hash(&self, hash: &Vec) -> Result { - let block = self.block_hashes.get(hash); - let payload = self.get_payload(&block.cloned())?; + let block = self + .block_hashes + .get(hash) + .ok_or(eyre!("Block Not Found"))?; + let payload = self.get_payload(&BlockTag::Number(*block))?; self.execution.get_block(&payload) } @@ -177,22 +190,26 @@ impl Node { self.consensus.get_header() } - fn get_payload(&self, block: &Option) -> Result { + fn get_payload(&self, block: &BlockTag) -> Result { match block { - Some(block) => { - let payload = self.payloads.get(block); - match payload { - Some(payload) => Ok(payload.clone()), - None => Err(eyre::eyre!("Block Not Found")), - } + BlockTag::Latest => { + let payload = self.payloads.get(&self.latest_block); + payload.cloned().ok_or(eyre!("Block Not Found")) } - None => { - let payload = self.payloads.get(&self.block_head); - match payload { - Some(payload) => Ok(payload.clone()), - None => Err(eyre::eyre!("Block Not Found")), - } + BlockTag::Finalized => { + let payload = self.payloads.get(&self.finalized_block); + payload.cloned().ok_or(eyre!("Block Not Found")) + } + BlockTag::Number(num) => { + let payload = self.payloads.get(&num); + payload.cloned().ok_or(eyre!("Block Not Found")) } } } } + +pub enum BlockTag { + Latest, + Finalized, + Number(u64), +} diff --git a/client/src/rpc.rs b/client/src/rpc.rs index c3a1f53..2eee486 100644 --- a/client/src/rpc.rs +++ b/client/src/rpc.rs @@ -13,7 +13,7 @@ use jsonrpsee::{ proc_macros::rpc, }; -use crate::node::Node; +use crate::node::{BlockTag, Node}; use common::utils::{hex_str_to_bytes, u64_to_hex_string}; use execution::types::{CallOpts, ExecutionBlock}; @@ -242,17 +242,18 @@ fn convert_err(res: Result) -> Result { }) } -fn decode_block(block: &str) -> Result> { +fn decode_block(block: &str) -> Result { match block { - "latest" => Ok(None), + "latest" => Ok(BlockTag::Latest), + "finalized" => Ok(BlockTag::Finalized), _ => { if block.starts_with("0x") { - Ok(Some(u64::from_str_radix( + Ok(BlockTag::Number(u64::from_str_radix( block.strip_prefix("0x").unwrap(), 16, )?)) } else { - Ok(Some(block.parse()?)) + Ok(BlockTag::Number(block.parse()?)) } } } diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index 131338a..40f12ec 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -5,7 +5,7 @@ use std::time::UNIX_EPOCH; use blst::min_pk::{PublicKey, Signature}; use blst::BLST_ERROR; use chrono::Duration; -use eyre::Result; +use eyre::{eyre, Result}; use log::info; use ssz_rs::prelude::*; @@ -53,7 +53,7 @@ impl ConsensusClient { header_hash.to_string() == format!("0x{}", hex::encode(checkpoint_block_root)); if !(header_valid && committee_valid) { - return Err(eyre::eyre!("Invalid Bootstrap")); + return Err(eyre!("Invalid Bootstrap")); } let store = Store { @@ -72,10 +72,20 @@ impl ConsensusClient { let slot = slot.unwrap_or(self.store.optimistic_header.slot); let mut block = self.rpc.get_block(slot).await?.clone(); let block_hash = block.hash_tree_root()?; - let verified_block_hash = self.store.optimistic_header.clone().hash_tree_root()?; + + let latest_slot = self.store.optimistic_header.slot; + let finalized_slot = self.store.finalized_header.slot; + + let verified_block_hash = if slot == latest_slot { + self.store.optimistic_header.clone().hash_tree_root()? + } else if slot == finalized_slot { + self.store.finalized_header.clone().hash_tree_root()? + } else { + return Err(eyre!("Block Not Found")); + }; if verified_block_hash != block_hash { - Err(eyre::eyre!("Block Root Mismatch")) + Err(eyre!("Block Root Mismatch")) } else { Ok(block.body.execution_payload) } @@ -127,13 +137,13 @@ impl ConsensusClient { if !(update_signature_period == store_period + 1 || update_signature_period == store_period) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } 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!("Invalid Update")); } let finality_branch_valid = is_finality_proof_valid( @@ -143,7 +153,7 @@ impl ConsensusClient { ); if !(finality_branch_valid) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let next_committee_branch_valid = is_next_committee_proof_valid( @@ -153,7 +163,7 @@ impl ConsensusClient { ); if !next_committee_branch_valid { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let sync_committee = if store_period == update_signature_period { @@ -169,7 +179,7 @@ impl ConsensusClient { let committee_quorum = pks.len() > 1; if !committee_quorum { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let header_root = bytes_to_bytes32(update.attested_header.hash_tree_root()?.as_bytes()); @@ -178,7 +188,7 @@ impl ConsensusClient { let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); if !is_valid_sig { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } Ok(()) @@ -190,13 +200,13 @@ impl ConsensusClient { if !(update_signature_period == store_period + 1 || update_signature_period == store_period) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } 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!("Invalid Update")); } let finality_branch_valid = is_finality_proof_valid( @@ -206,7 +216,7 @@ impl ConsensusClient { ); if !(finality_branch_valid) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let sync_committee = &self.store.current_sync_committee; @@ -218,7 +228,7 @@ impl ConsensusClient { let committee_quorum = pks.len() > 1; if !committee_quorum { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let header_root = @@ -228,7 +238,7 @@ impl ConsensusClient { let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); if !is_valid_sig { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } Ok(()) @@ -240,11 +250,11 @@ impl ConsensusClient { if !(update_signature_period == store_period + 1 || update_signature_period == store_period) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } if !(update.signature_slot > update.attested_header.slot) { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let sync_committee = &self.store.current_sync_committee; @@ -256,7 +266,7 @@ impl ConsensusClient { let committee_quorum = pks.len() > 1; if !committee_quorum { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } let header_root = @@ -266,7 +276,7 @@ impl ConsensusClient { let is_valid_sig = is_aggregate_valid(sig, signing_root.as_bytes(), &pks); if !is_valid_sig { - return Err(eyre::eyre!("Invalid Update")); + return Err(eyre!("Invalid Update")); } Ok(())