1use std::{convert::TryInto, str::FromStr, sync::Arc, time::Duration};
3
4use anyhow::anyhow;
5use crossbeam::channel;
6use rand::{rngs::OsRng, Rng};
7use sha2::{Digest, Sha256};
8use slog::{debug, error, info};
9use tendermint::merkle::HASH_SIZE;
10use tendermint_light_client::{
11 builder::LightClientBuilder,
12 components::{self, io::AtHeight, verifier::PredicateVerifier},
13 instance::Instance,
14 light_client,
15 operations::{ProdCommitValidator, ProvidedVotingPowerCalculator},
16 store::LightStore,
17 types::{
18 Hash as TMHash, LightBlock as TMLightBlock, PeerId, Status, Time, TrustThreshold,
19 TrustedBlockState,
20 },
21 verifier::{predicates::ProdPredicates, Verdict, Verifier as TMVerifier},
22};
23
24use crate::{
25 common::{logger::get_logger, namespace::Namespace, process, time, version::Version},
26 consensus::{
27 beacon::EpochTime,
28 registry::METHOD_PROVE_FRESHNESS,
29 roothash::Header,
30 state::ConsensusState,
31 tendermint::{
32 chain_id, decode_light_block, merkle, state_root_from_header,
33 verifier::{
34 clock::InsecureClock,
35 io::Io,
36 store::LruStore,
37 types::{Command, Nonce, NONCE_SIZE},
38 },
39 LightBlockMeta,
40 },
41 transaction::{Proof, SignedTransaction, Transaction},
42 verifier::{self, verify_state_freshness, Error, TrustRoot},
43 BlockMetadata, Event, LightBlock, HEIGHT_LATEST, METHOD_META,
44 },
45 future::block_on,
46 host::Host,
47 protocol::Protocol,
48 storage::mkvs::{Root, RootType},
49 types::{Body, EventKind, HostFetchConsensusEventsRequest, HostFetchConsensusEventsResponse},
50};
51
52use self::{
53 cache::Cache,
54 handle::Handle,
55 store::{TrustedState, TrustedStateStore},
56};
57
58mod cache;
60mod clock;
61mod handle;
62mod io;
63mod noop;
64mod predicates;
65mod signature;
66mod store;
67mod types;
68
69pub use noop::NopVerifier;
71
72const MAX_INITIALIZATION_RETRIES: usize = 3;
74
75const TRUSTED_STATE_SAVE_INTERVAL: u64 = 128;
77
78pub struct Verifier {
80 logger: slog::Logger,
81 protocol: Arc<Protocol>,
82 tokio_runtime: tokio::runtime::Handle,
83 runtime_version: Version,
84 runtime_id: Namespace,
85 chain_context: String,
86 trust_root: TrustRoot,
87 command_sender: channel::Sender<Command>,
88 command_receiver: channel::Receiver<Command>,
89 trusted_state_store: TrustedStateStore,
90}
91
92impl Verifier {
93 pub fn new(
95 protocol: Arc<Protocol>,
96 tokio_runtime: tokio::runtime::Handle,
97 trust_root: TrustRoot,
98 runtime_id: Namespace,
99 chain_context: String,
100 ) -> Self {
101 let logger = get_logger("consensus/cometbft/verifier");
102 let (command_sender, command_receiver) = channel::unbounded();
103 let runtime_version = protocol.get_config().version;
104 let trusted_state_store =
105 TrustedStateStore::new(runtime_id, chain_context.clone(), protocol.clone());
106
107 assert_eq!(
108 trust_root.runtime_id, runtime_id,
109 "trust root must have the same runtime id"
110 );
111
112 Self {
113 logger,
114 protocol,
115 tokio_runtime,
116 runtime_version,
117 runtime_id,
118 chain_context,
119 trust_root,
120 command_sender,
121 command_receiver,
122 trusted_state_store,
123 }
124 }
125
126 pub fn handle(&self) -> impl verifier::Verifier {
128 Handle {
129 protocol: self.protocol.clone(),
130 command_sender: self.command_sender.clone(),
131 }
132 }
133
134 fn verify_to_target(
135 &self,
136 height: u64,
137 cache: &mut Cache,
138 instance: &mut Instance,
139 ) -> Result<TMLightBlock, Error> {
140 let verified_block = match height {
141 HEIGHT_LATEST => instance.light_client.verify_to_highest(&mut instance.state),
142 _ => instance
143 .light_client
144 .verify_to_target(height.try_into().unwrap(), &mut instance.state),
145 }
146 .map_err(|err| Error::VerificationFailed(err.into()))?;
147
148 instance.state.verification_trace.clear();
150
151 cache.update_verified_block(&verified_block);
152 self.update_insecure_posix_time(&verified_block);
153
154 Ok(verified_block)
155 }
156
157 fn sync(&self, cache: &mut Cache, instance: &mut Instance, height: u64) -> Result<(), Error> {
158 if height < cache.last_verified_height || height < cache.latest_known_height().unwrap_or(0)
159 {
160 return Ok(());
162 }
163 self.verify_to_target(height, cache, instance)?;
164 Ok(())
165 }
166
167 fn latest_consensus_state(
168 &self,
169 cache: &mut Cache,
170 instance: &mut Instance,
171 ) -> Result<ConsensusState, Error> {
172 let height = self.latest_consensus_height(cache)?;
174 let state_root = self.state_root_from_metadata(cache, instance, height)?;
175
176 Ok(ConsensusState::from_protocol(
177 self.protocol.clone(),
178 state_root.version,
179 state_root,
180 ))
181 }
182
183 fn latest_consensus_height(&self, cache: &Cache) -> Result<u64, Error> {
184 let height = cache.latest_known_height().ok_or(Error::Internal)?;
185 Ok(height)
186 }
187
188 fn consensus_state_at(
189 &self,
190 cache: &mut Cache,
191 instance: &mut Instance,
192 height: u64,
193 ) -> Result<ConsensusState, Error> {
194 let state_root = match self.verify_to_target(height, cache, instance) {
197 Ok(verified_block) => state_root_from_header(&verified_block.signed_header),
198 Err(_) => self.state_root_from_metadata(cache, instance, height - 1)?,
199 };
200
201 Ok(ConsensusState::from_protocol(
202 self.protocol.clone(),
203 state_root.version + 1,
204 state_root,
205 ))
206 }
207
208 fn verify_consensus_block(
209 &self,
210 cache: &mut Cache,
211 instance: &mut Instance,
212 consensus_block: LightBlock,
213 ) -> Result<LightBlockMeta, Error> {
214 let lb_height = consensus_block.height;
216 let untrusted_block =
217 decode_light_block(consensus_block).map_err(Error::VerificationFailed)?;
218 let untrusted_header = untrusted_block
219 .signed_header
220 .as_ref()
221 .ok_or_else(|| Error::VerificationFailed(anyhow!("missing signed header")))?;
222
223 let height = untrusted_header.header().height.value();
226 if height != lb_height {
227 return Err(Error::VerificationFailed(anyhow!(
228 "inconsistent light block/header height"
229 )));
230 }
231 let verified_block = self.verify_to_target(height, cache, instance)?;
232
233 if untrusted_header.header() != verified_block.signed_header.header() {
235 return Err(Error::VerificationFailed(anyhow!("header mismatch")));
236 }
237
238 Ok(untrusted_block)
239 }
240
241 fn verify_freshness_with_rak(
243 &self,
244 state: &ConsensusState,
245 cache: &Cache,
246 ) -> Result<(), Error> {
247 let identity = if let Some(identity) = self.protocol.get_identity() {
248 identity
249 } else {
250 return Ok(());
251 };
252
253 verify_state_freshness(
254 state,
255 identity,
256 &self.runtime_id,
257 &self.runtime_version,
258 &cache.host_node_id,
259 )
260 }
261
262 fn verify_freshness_with_proof(
271 &self,
272 instance: &mut Instance,
273 cache: &mut Cache,
274 ) -> Result<(), Error> {
275 info!(
276 self.logger,
277 "Verifying state freshness using prove freshness transaction"
278 );
279
280 let mut rng = OsRng {};
282 let mut nonce = [0u8; NONCE_SIZE];
283 rng.fill(&mut nonce);
284
285 let io = Io::new(&self.protocol);
287 let stwp = io.fetch_freshness_proof(&nonce).map_err(|err| {
288 Error::FreshnessVerificationFailed(anyhow!("failed to fetch freshness proof: {}", err))
289 })?;
290
291 let tx = self.verify_transaction(cache, instance, &stwp.signed_tx, &stwp.proof)?;
293
294 if tx.method != METHOD_PROVE_FRESHNESS {
296 return Err(Error::FreshnessVerificationFailed(anyhow!(
297 "invalid method name"
298 )));
299 }
300
301 let tx_nonce: Nonce = cbor::from_value(tx.body).map_err(|err| {
302 Error::FreshnessVerificationFailed(anyhow!("failed to decode nonce: {}", err))
303 })?;
304 match nonce.cmp(&tx_nonce) {
305 std::cmp::Ordering::Equal => (),
306 _ => return Err(Error::FreshnessVerificationFailed(anyhow!("invalid nonce"))),
307 }
308
309 info!(self.logger, "State freshness successfully verified");
310
311 Ok(())
312 }
313
314 fn verify_transaction(
315 &self,
316 cache: &mut Cache,
317 instance: &mut Instance,
318 signed_tx: &SignedTransaction,
319 proof: &Proof,
320 ) -> Result<Transaction, Error> {
321 if !signed_tx.verify(&self.chain_context) {
323 return Err(Error::TransactionVerificationFailed(anyhow!(
324 "failed to verify the signature"
325 )));
326 }
327
328 let verified_block = self
330 .verify_to_target(proof.height, cache, instance)
331 .map_err(|err| {
332 Error::TransactionVerificationFailed(anyhow!("failed to fetch the block: {}", err))
333 })?;
334
335 let header = verified_block.signed_header.header;
336 if header.height.value() != proof.height {
337 return Err(Error::TransactionVerificationFailed(anyhow!(
338 "invalid block"
339 )));
340 }
341
342 let root_hash = header
343 .data_hash
344 .ok_or_else(|| Error::TransactionVerificationFailed(anyhow!("root hash not found")))?;
345 let root_hash = match root_hash {
346 TMHash::Sha256(hash) => hash,
347 TMHash::None => {
348 return Err(Error::TransactionVerificationFailed(anyhow!(
349 "root hash not found"
350 )));
351 }
352 };
353
354 let digest = Sha256::digest(cbor::to_vec(signed_tx.clone()));
356 let mut tx_hash = [0u8; HASH_SIZE];
357 tx_hash.copy_from_slice(&digest);
358
359 let merkle_proof: merkle::Proof = cbor::from_slice(&proof.raw_proof).map_err(|err| {
361 Error::TransactionVerificationFailed(anyhow!("failed to decode Merkle proof: {}", err))
362 })?;
363
364 merkle_proof.verify(root_hash, tx_hash).map_err(|err| {
365 Error::TransactionVerificationFailed(anyhow!("failed to verify Merkle proof: {}", err))
366 })?;
367
368 let tx: Transaction = cbor::from_slice(signed_tx.blob.as_slice()).map_err(|err| {
370 Error::TransactionVerificationFailed(anyhow!("failed to decode transaction: {}", err))
371 })?;
372
373 Ok(tx)
374 }
375
376 fn state_root_from_metadata(
378 &self,
379 cache: &mut Cache,
380 instance: &mut Instance,
381 height: u64,
382 ) -> Result<Root, Error> {
383 debug!(
384 self.logger,
385 "Fetching state root from block metadata transaction"
386 );
387
388 let io = Io::new(&self.protocol);
390 let stwp = io.fetch_block_metadata(height).map_err(|err| {
391 Error::StateRoot(anyhow!(
392 "failed to fetch block metadata transaction: {}",
393 err
394 ))
395 })?;
396
397 let tx = self.verify_transaction(cache, instance, &stwp.signed_tx, &stwp.proof)?;
399
400 if tx.method != METHOD_META {
401 return Err(Error::StateRoot(anyhow!("invalid method name")));
402 }
403
404 let meta: BlockMetadata = cbor::from_value(tx.body).map_err(|err| {
405 Error::StateRoot(anyhow!(
406 "failed to decode block metadata transaction: {}",
407 err
408 ))
409 })?;
410
411 Ok(Root {
412 namespace: Namespace::default(),
413 version: height,
414 root_type: RootType::State,
415 hash: meta.state_root,
416 })
417 }
418
419 fn verify(
420 &self,
421 cache: &mut Cache,
422 instance: &mut Instance,
423 consensus_block: LightBlock,
424 runtime_header: Header,
425 epoch: EpochTime,
426 ) -> Result<ConsensusState, Error> {
427 predicates::verify_namespace(self.runtime_id, &runtime_header)?;
429 predicates::verify_round_advance(cache, &runtime_header, &consensus_block, epoch)?;
430 predicates::verify_consensus_advance(cache, &consensus_block)?;
431
432 let height = consensus_block.height;
434 let consensus_block = self.verify_consensus_block(cache, instance, consensus_block)?;
435
436 predicates::verify_time(&runtime_header, &consensus_block)?;
438
439 let state = self.consensus_state_at(cache, instance, height)?;
441
442 if let Some((state_root, state_epoch)) =
444 cache.verified_state_roots.get(&runtime_header.round)
445 {
446 if state_root == &runtime_header.state_root
447 && state_epoch == &epoch
448 && epoch == cache.last_verified_epoch
449 {
450 cache.last_verified_height = height;
454 cache.last_verified_round = runtime_header.round;
455
456 return Ok(state);
457 }
458
459 }
461
462 let next_state = self.consensus_state_at(cache, instance, height + 1)?;
466
467 predicates::verify_state_root(&next_state, &runtime_header)?;
469 predicates::verify_epoch(&next_state, epoch)?;
470
471 if cache.last_verified_epoch != epoch {
474 let latest_state = self.latest_consensus_state(cache, instance)?;
475 self.verify_freshness_with_rak(&latest_state, cache)?;
476 }
477
478 cache
480 .verified_state_roots
481 .put(runtime_header.round, (runtime_header.state_root, epoch));
482
483 cache.last_verified_height = height;
485 cache.last_verified_round = runtime_header.round;
486 cache.last_verified_epoch = epoch;
487
488 Ok(state)
489 }
490
491 fn verify_for_query(
492 &self,
493 cache: &mut Cache,
494 instance: &mut Instance,
495 consensus_block: LightBlock,
496 runtime_header: Header,
497 epoch: EpochTime,
498 ) -> Result<ConsensusState, Error> {
499 predicates::verify_namespace(self.runtime_id, &runtime_header)?;
501
502 let height = consensus_block.height;
504 let consensus_block = self.verify_consensus_block(cache, instance, consensus_block)?;
505
506 predicates::verify_time(&runtime_header, &consensus_block)?;
508
509 let state = self.consensus_state_at(cache, instance, height)?;
511
512 if let Some((state_root, state_epoch)) =
514 cache.verified_state_roots.get(&runtime_header.round)
515 {
516 if state_root == &runtime_header.state_root && state_epoch == &epoch {
517 return Ok(state);
519 }
520
521 }
523
524 let next_state = self.consensus_state_at(cache, instance, height + 1)?;
528
529 predicates::verify_state_root(&next_state, &runtime_header)?;
531 predicates::verify_epoch(&next_state, epoch)?;
532
533 cache
535 .verified_state_roots
536 .put(runtime_header.round, (runtime_header.state_root, epoch));
537
538 Ok(state)
539 }
540
541 fn events_at(&self, height: u64, kind: EventKind) -> Result<Vec<Event>, Error> {
542 let result = self
543 .protocol
544 .call_host(Body::HostFetchConsensusEventsRequest(
545 HostFetchConsensusEventsRequest { height, kind },
546 ))
547 .map_err(|err| Error::VerificationFailed(err.into()))?;
548 match result {
551 Body::HostFetchConsensusEventsResponse(HostFetchConsensusEventsResponse { events }) => {
552 Ok(events)
553 }
554 _ => Err(Error::VerificationFailed(anyhow!("bad response from host"))),
555 }
556 }
557
558 fn update_insecure_posix_time(&self, verified_block: &TMLightBlock) {
559 time::update_insecure_posix_time(
563 verified_block
564 .signed_header
565 .header
566 .time
567 .duration_since(Time::unix_epoch())
568 .unwrap()
569 .as_secs()
570 .try_into()
571 .unwrap(),
572 );
573 }
574
575 pub fn start(self) {
577 std::thread::spawn(move || {
578 let _guard = self.tokio_runtime.enter(); let logger = get_logger("consensus/cometbft/verifier");
581 info!(logger, "Starting consensus verifier");
582
583 for retry in 1..=MAX_INITIALIZATION_RETRIES {
588 let result =
590 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| self.run())) {
591 Ok(result) => result,
592 Err(_) => {
593 error!(logger, "Consensus verifier aborted");
594 process::abort();
595 }
596 };
597
598 match result {
600 Ok(_) => {}
601 Err(err @ Error::Builder(_))
602 | Err(err @ Error::TrustedStateLoadingFailed)
603 | Err(err @ Error::ChainContextTransitionFailed(_)) => {
604 error!(logger, "Consensus verifier failed to initialize, retrying";
605 "err" => %err,
606 "retry" => retry,
607 );
608 }
609 Err(err) => {
610 error!(logger, "Consensus verifier terminated, aborting";
612 "err" => %err,
613 );
614 process::abort();
615 }
616 }
617
618 std::thread::sleep(Duration::from_secs(1));
620 }
621
622 error!(logger, "Failed to start consensus verifier, aborting");
623 process::abort();
624 });
625 }
626
627 fn run(&self) -> Result<(), Error> {
628 let options = light_client::Options {
630 trust_threshold: Default::default(),
631 trusting_period: Duration::from_secs(3600 * 24 * 365 * 10), clock_drift: Duration::from_secs(60),
635 };
636
637 let peer_id = PeerId::new([0; 20]);
639 let clock = Box::new(InsecureClock);
640 let verifier = Box::new(PredicateVerifier::new(
641 ProdPredicates,
642 ProvidedVotingPowerCalculator::<signature::DomSepVerifier>::default(),
643 ProdCommitValidator,
644 ));
645 let io = Box::new(Io::new(&self.protocol));
646
647 info!(self.logger, "Loading trusted state");
650 let trusted_state = self
651 .trusted_state_store
652 .load(self.runtime_version, &self.trust_root);
653
654 let trusted_state: TrustedState = match trusted_state {
655 Ok(state) => state,
656 Err(err) => {
657 error!(self.logger, "failed to load trusted state (if running in SGX mode, check if the CPU had changed; if yes, wipe 'worker-local-storage.badger.db' and restart the node)");
658 return Err(err);
659 }
660 };
661
662 info!(self.logger, "Checking chain context change");
665 let trusted_state = self.handle_chain_context_change(
666 trusted_state,
667 verifier.as_ref(),
668 clock.as_ref(),
669 io.as_ref(),
670 )?;
671
672 let mut store = Box::new(LruStore::new(
674 512,
675 trusted_state.trust_root.height.try_into().unwrap(),
676 ));
677 for lb in trusted_state.trusted_blocks {
678 store.insert(lb.into(), Status::Trusted);
679 }
680 let trust_root = trusted_state.trust_root;
681
682 let builder = LightClientBuilder::custom(
683 peer_id,
684 options,
685 store,
686 io,
687 clock,
688 verifier,
689 Box::new(components::scheduler::basic_bisecting_schedule),
690 Box::new(ProdPredicates),
691 );
692
693 let mut instance = builder
694 .trust_primary_at(
695 trust_root.height.try_into().unwrap(),
696 TMHash::from_str(&trust_root.hash.to_uppercase()).unwrap(),
697 )
698 .map_err(|err| Error::Builder(err.into()))?
699 .build();
700
701 info!(self.logger, "Consensus verifier initialized";
702 "trust_root_height" => trust_root.height,
703 "trust_root_hash" => ?trust_root.hash,
704 "trust_root_runtime_id" => ?trust_root.runtime_id,
705 "trust_root_chain_context" => ?trust_root.chain_context,
706 );
707
708 let host_node_id =
709 block_on(self.protocol.identity()).expect("host should provide a node identity");
710
711 let mut cache = Cache::new(host_node_id);
712
713 let verified_block = self.verify_to_target(HEIGHT_LATEST, &mut cache, &mut instance)?;
716
717 self.trusted_state_store
718 .save(self.runtime_version, &instance.state.light_store);
719
720 let mut last_saved_verified_block_height =
721 verified_block.signed_header.header.height.value();
722
723 info!(self.logger, "Consensus verifier synced";
724 "latest_height" => cache.latest_known_height(),
725 );
726
727 self.verify_freshness_with_proof(&mut instance, &mut cache)?;
731
732 loop {
734 let command = self.command_receiver.recv().map_err(|_| Error::Internal)?;
735
736 match command {
737 Command::Synchronize(height, sender) => {
738 sender
739 .send(self.sync(&mut cache, &mut instance, height))
740 .map_err(|_| Error::Internal)?;
741 }
742 Command::Verify(consensus_block, runtime_header, epoch, sender, false) => {
743 sender
744 .send(self.verify(
745 &mut cache,
746 &mut instance,
747 consensus_block,
748 runtime_header,
749 epoch,
750 ))
751 .map_err(|_| Error::Internal)?;
752 }
753 Command::Verify(consensus_block, runtime_header, epoch, sender, true) => {
754 sender
755 .send(self.verify_for_query(
756 &mut cache,
757 &mut instance,
758 consensus_block,
759 runtime_header,
760 epoch,
761 ))
762 .map_err(|_| Error::Internal)?;
763 }
764 Command::LatestState(sender) => {
765 sender
766 .send(self.latest_consensus_state(&mut cache, &mut instance))
767 .map_err(|_| Error::Internal)?;
768 }
769 Command::StateAt(height, sender) => {
770 sender
771 .send(self.consensus_state_at(&mut cache, &mut instance, height))
772 .map_err(|_| Error::Internal)?;
773 }
774 Command::LatestHeight(sender) => {
775 sender
776 .send(self.latest_consensus_height(&cache))
777 .map_err(|_| Error::Internal)?;
778 }
779 Command::EventsAt(height, kind, sender) => {
780 sender
781 .send(self.events_at(height, kind))
782 .map_err(|_| Error::Internal)?;
783 }
784 }
785
786 if let Some(last_verified_block) = cache.last_verified_block.as_ref() {
788 let last_height = last_verified_block.signed_header.header.height.into();
789 if last_height - last_saved_verified_block_height > TRUSTED_STATE_SAVE_INTERVAL {
790 self.trusted_state_store
791 .save(self.runtime_version, &instance.state.light_store);
792 last_saved_verified_block_height = last_height;
793 }
794 }
795 }
796 }
797
798 fn handle_chain_context_change(
799 &self,
800 mut trusted_state: TrustedState,
801 verifier: &impl TMVerifier,
802 clock: &impl components::clock::Clock,
803 io: &Io,
804 ) -> Result<TrustedState, Error> {
805 let host_info = self.protocol.get_host_info();
806
807 if trusted_state.trust_root.chain_context == host_info.consensus_chain_context {
809 info!(self.logger, "Consensus chain context hasn't changed");
810 return Ok(trusted_state);
811 }
812 info!(self.logger, "Consensus chain context has changed");
813
814 let trusted_block: TMLightBlock = trusted_state
818 .trusted_blocks
819 .pop()
820 .ok_or_else(|| {
821 Error::ChainContextTransitionFailed(anyhow!(
822 "cannot transition from embedded trust root"
823 ))
824 })?
825 .into();
826
827 let height = io
831 .fetch_genesis_height()
832 .map_err(|err| Error::ChainContextTransitionFailed(err.into()))?;
833 let height = AtHeight::At(height.try_into().unwrap());
834 let untrusted_block = components::io::Io::fetch_light_block(io, height)
835 .map_err(|err| Error::ChainContextTransitionFailed(err.into()))?;
836
837 if untrusted_block.signed_header.header.last_block_id.is_some() {
838 return Err(Error::ChainContextTransitionFailed(anyhow!(
839 "invalid genesis block"
840 )));
841 }
842
843 let untrusted = untrusted_block.as_untrusted_state();
844
845 let header = trusted_block.signed_header.header;
853 let height = header.height;
854 let height = if height.increment() != untrusted.height() {
855 height
856 } else {
857 height
858 .value()
859 .checked_sub(1)
860 .ok_or_else(|| Error::ChainContextTransitionFailed(anyhow!("height underflow")))?
861 .try_into()
862 .unwrap()
863 };
864
865 let trusted = TrustedBlockState {
866 header_time: header.time,
867 height,
868 next_validators: &trusted_block.validators,
869 next_validators_hash: header.validators_hash,
870 chain_id: &chain_id(&host_info.consensus_chain_context),
872 };
873
874 let options = light_client::Options {
876 trust_threshold: TrustThreshold::TWO_THIRDS,
877 trusting_period: Duration::from_secs(3600 * 24 * 365 * 10), clock_drift: Duration::from_secs(60),
879 };
880 let now = clock.now();
881
882 let verdict = verifier.verify_update_header(untrusted, trusted, &options, now);
883
884 match verdict {
885 Verdict::Success => (),
886 Verdict::NotEnoughTrust(tally) => {
887 info!(
888 self.logger,
889 "Not enough trust to accept new chain context";
890 "log_event" => "consensus/cometbft/verifier/chain_context/no_trust",
891 "tally" => ?tally,
892 );
893 return Err(Error::ChainContextTransitionFailed(anyhow!(
894 "not enough trust"
895 )));
896 }
897 Verdict::Invalid(e) => {
898 info!(
899 self.logger,
900 "Failed to accept new chain context";
901 "log_event" => "consensus/cometbft/verifier/chain_context/failed",
902 "error" => ?e,
903 );
904 return Err(Error::ChainContextTransitionFailed(anyhow!(
905 "invalid genesis block"
906 )));
907 }
908 }
909
910 info!(self.logger, "Consensus chain context transition done");
911
912 let header = &untrusted_block.signed_header.header;
913 let trust_root = TrustRoot {
914 height: header.height.into(),
915 hash: header.hash().to_string(),
916 runtime_id: self.runtime_id,
917 chain_context: host_info.consensus_chain_context,
918 };
919
920 Ok(TrustedState {
921 trust_root,
922 trusted_blocks: vec![untrusted_block.into()],
923 })
924 }
925}