From 95021a66d56cffd4afd7cdd67560a8961d4f1e1b Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 13 Sep 2022 11:25:12 +0200 Subject: [PATCH] runtime/consensus/tendermint/verifier: Refactor verification to target --- .changelog/4934.internal.md | 1 + .../consensus/tendermint/verifier/cache.rs | 41 ++++--- .../src/consensus/tendermint/verifier/mod.rs | 104 +++++++++--------- 3 files changed, 76 insertions(+), 70 deletions(-) create mode 100644 .changelog/4934.internal.md diff --git a/.changelog/4934.internal.md b/.changelog/4934.internal.md new file mode 100644 index 00000000000..d243e2b91c0 --- /dev/null +++ b/.changelog/4934.internal.md @@ -0,0 +1 @@ +runtime/consensus/tendermint/verifier: Refactor verifier diff --git a/runtime/src/consensus/tendermint/verifier/cache.rs b/runtime/src/consensus/tendermint/verifier/cache.rs index f2d55f6e234..94471218f2c 100644 --- a/runtime/src/consensus/tendermint/verifier/cache.rs +++ b/runtime/src/consensus/tendermint/verifier/cache.rs @@ -7,37 +7,42 @@ pub struct Cache { pub last_verified_height: u64, pub last_verified_round: u64, pub last_verified_epoch: u64, - pub last_verified_block: TMLightBlock, + pub last_verified_block: Option, pub verified_state_roots: lru::LruCache, pub verified_state_roots_queries: lru::LruCache, pub node_id: Option, } impl Cache { - pub fn new(verified_block: TMLightBlock) -> Self { + /// Latest known and verified consensus layer height. + pub fn latest_known_height(&self) -> Option { + self.last_verified_block + .as_ref() + .map(|b| b.signed_header.header.height.value()) + } + + /// Process a new verified consensus layer block and update the cache if needed. + pub fn update_verified_block(&mut self, verified_block: &TMLightBlock) { + let h = |b: &TMLightBlock| -> Height { b.signed_header.header.height }; + if let Some(last_verified_block) = self.last_verified_block.as_ref() { + if h(verified_block) <= h(last_verified_block) { + return; + } + } + self.last_verified_block = Some(verified_block.clone()) + } +} + +impl Default for Cache { + fn default() -> Self { Self { last_verified_height: 0, last_verified_round: 0, last_verified_epoch: 0, - last_verified_block: verified_block, + last_verified_block: None, verified_state_roots: lru::LruCache::new(128), verified_state_roots_queries: lru::LruCache::new(128), node_id: None, } } } - -impl Cache { - /// Latest known and verified consensus layer height. - pub fn latest_known_height(&self) -> u64 { - self.last_verified_block.signed_header.header.height.value() - } - - /// Process a new verified consensus layer block and update the cache if needed. - pub fn update_verified_block(&mut self, verified_block: TMLightBlock) { - let h = |b: &TMLightBlock| -> Height { b.signed_header.header.height }; - if h(&verified_block) > h(&self.last_verified_block) { - self.last_verified_block = verified_block - } - } -} diff --git a/runtime/src/consensus/tendermint/verifier/mod.rs b/runtime/src/consensus/tendermint/verifier/mod.rs index 45432c3f8d2..badfa44c08c 100644 --- a/runtime/src/consensus/tendermint/verifier/mod.rs +++ b/runtime/src/consensus/tendermint/verifier/mod.rs @@ -45,7 +45,7 @@ use crate::{ }, transaction::{Transaction, SIGNATURE_CONTEXT}, verifier::{self, verify_state_freshness, Error, TrustRoot, TrustedState}, - Event, LightBlock, + Event, LightBlock, HEIGHT_LATEST, }, protocol::Protocol, types::{Body, EventKind, HostFetchConsensusEventsRequest, HostFetchConsensusEventsResponse}, @@ -125,19 +125,33 @@ impl Verifier { } } - fn sync(&self, cache: &mut Cache, instance: &mut Instance, height: u64) -> Result<(), Error> { - if height < cache.last_verified_height || height < cache.latest_known_height() { - // Ignore requests for earlier heights. - return Ok(()); + fn verify_to_target( + &self, + height: u64, + cache: &mut Cache, + instance: &mut Instance, + ) -> Result { + let verified_block = match height { + HEIGHT_LATEST => instance.light_client.verify_to_highest(&mut instance.state), + _ => instance + .light_client + .verify_to_target(height.try_into().unwrap(), &mut instance.state), } + .map_err(|err| Error::VerificationFailed(err.into()))?; - let verified_block = instance - .light_client - .verify_to_target(height.try_into().unwrap(), &mut instance.state) - .map_err(|err| Error::VerificationFailed(err.into()))?; + cache.update_verified_block(&verified_block); self.update_insecure_posix_time(&verified_block); - cache.update_verified_block(verified_block); + Ok(verified_block) + } + + fn sync(&self, cache: &mut Cache, instance: &mut Instance, height: u64) -> Result<(), Error> { + if height < cache.last_verified_height || height < cache.latest_known_height().unwrap_or(0) + { + // Ignore requests for earlier heights. + return Ok(()); + } + self.verify_to_target(height, cache, instance)?; Ok(()) } @@ -146,12 +160,13 @@ impl Verifier { cache: &mut Cache, instance: &mut Instance, ) -> Result { - let height = cache.latest_known_height(); + let height = self.latest_consensus_height(cache)?; self.consensus_state_at(cache, instance, height) } fn latest_consensus_height(&self, cache: &Cache) -> Result { - Ok(cache.latest_known_height()) + let height = cache.latest_known_height().ok_or(Error::Internal)?; + Ok(height) } fn consensus_state_at( @@ -160,15 +175,9 @@ impl Verifier { instance: &mut Instance, height: u64, ) -> Result { - let verified_block = instance - .light_client - .verify_to_target(height.try_into().unwrap(), &mut instance.state) - .map_err(|err| Error::VerificationFailed(err.into()))?; + let verified_block = self.verify_to_target(height, cache, instance)?; let state_root = state_root_from_header(&verified_block.signed_header); - self.update_insecure_posix_time(&verified_block); - cache.update_verified_block(verified_block); - Ok(ConsensusState::from_protocol( self.protocol.clone(), state_root.version + 1, @@ -199,19 +208,15 @@ impl Verifier { // Verify up to the block at current height. // Only does forward verification and fails if height is lower than the last trust height. - let verified_block = instance - .light_client - .verify_to_target(untrusted_header.header().height, &mut instance.state) - .map_err(|err| Error::VerificationFailed(err.into()))?; + let height = untrusted_header.header().height.value(); + let verified_block = self.verify_to_target(height, cache, instance)?; // Validate passed consensus block. if untrusted_header != &verified_block.signed_header { return Err(Error::VerificationFailed(anyhow!("header mismatch"))); } - cache.last_verified_height = verified_block.signed_header.header.height.into(); - self.update_insecure_posix_time(&verified_block); - cache.update_verified_block(verified_block); + cache.last_verified_height = height; Ok(untrusted_block) } @@ -239,7 +244,11 @@ impl Verifier { /// in a block, the host replies with block's height, transaction details and a Merkle proof /// that the transaction was included in the block. In the final step, the verifier verifies /// the proof and accepts state as fresh iff verification succeeds. - fn verify_freshness_with_proof(&self, instance: &mut Instance) -> Result<(), Error> { + fn verify_freshness_with_proof( + &self, + instance: &mut Instance, + cache: &mut Cache, + ) -> Result<(), Error> { info!( self.logger, "Verifying state freshness using prove freshness transaction" @@ -285,14 +294,13 @@ impl Verifier { } // Fetch the block in which the transaction was published. - let block = instance - .light_client - .verify_to_target(height.try_into().unwrap(), &mut instance.state) + let verified_block = self + .verify_to_target(height, cache, instance) .map_err(|err| { Error::FreshnessVerificationFailed(anyhow!("failed to fetch the block: {}", err)) })?; - let header = block.signed_header.header; + let header = verified_block.signed_header.header; if header.height.value() != height { return Err(Error::VerificationFailed(anyhow!("invalid block"))); } @@ -462,22 +470,15 @@ impl Verifier { // Verify up to the block at current height. // Only does forward verification and fails if height is lower than the last trust height. - let verified_block = instance - .light_client - .verify_to_target(untrusted_header.header().height, &mut instance.state) - .map_err(|err| Error::VerificationFailed(err.into()))?; + let height = untrusted_header.header().height.value(); + let verified_block = self.verify_to_target(height, cache, instance)?; // Validate passed consensus block. if untrusted_header != &verified_block.signed_header { return Err(Error::VerificationFailed(anyhow!("header mismatch"))); } - self.update_insecure_posix_time(&verified_block); - cache.update_verified_block(verified_block); - - let consensus_block = untrusted_block; - - let state_root = consensus_block.get_state_root(); + let state_root = untrusted_block.get_state_root(); let state = ConsensusState::from_protocol( self.protocol.clone(), state_root.version + 1, @@ -703,19 +704,16 @@ impl Verifier { "trust_root_chain_context" => ?trust_root.chain_context, ); + let mut cache = Cache::default(); + // Sync the verifier up to the latest block to make sure we are up to date before // processing any requests. - let verified_block = instance - .light_client - .verify_to_highest(&mut instance.state) - .map_err(|err| Error::VerificationFailed(err.into()))?; + let verified_block = self.verify_to_target(HEIGHT_LATEST, &mut cache, &mut instance)?; self.trusted_state_store.save(&verified_block); - self.update_insecure_posix_time(&verified_block); let mut last_saved_verified_block_height = verified_block.signed_header.header.height.value(); - let mut cache = Cache::new(verified_block); info!(self.logger, "Consensus verifier synced"; "latest_height" => cache.latest_known_height(), @@ -725,7 +723,7 @@ impl Verifier { // as executors and key managers verify freshness regularly using node registration // (RAK with random nonces). if self.protocol.get_config().freshness_proofs { - self.verify_freshness_with_proof(&mut instance)?; + self.verify_freshness_with_proof(&mut instance, &mut cache)?; }; // Start the command processing loop. @@ -788,10 +786,12 @@ impl Verifier { } // Persist last verified block once in a while. - let last_height = cache.latest_known_height(); - if last_height - last_saved_verified_block_height > TRUSTED_STATE_SAVE_INTERVAL { - self.trusted_state_store.save(&cache.last_verified_block); - last_saved_verified_block_height = last_height; + if let Some(last_verified_block) = cache.last_verified_block.as_ref() { + let last_height = last_verified_block.signed_header.header.height.into(); + if last_height - last_saved_verified_block_height > TRUSTED_STATE_SAVE_INTERVAL { + self.trusted_state_store.save(last_verified_block); + last_saved_verified_block_height = last_height; + } } } }