1#![cfg(any(test, feature = "testing"))]
2
3use std::{
4 cmp::max,
5 collections::{HashMap, HashSet},
6 sync::Arc,
7};
8
9use alloy::primitives::{Address, U256};
10use anyhow::{Context, Result, bail, ensure};
11use async_lock::Mutex;
12use bitvec::vec::BitVec;
13use committable::{Commitment, Committable};
14use derivative::Derivative;
15use espresso_types::{
16 BLOCK_MERKLE_TREE_HEIGHT, BlockMerkleTree, EpochVersion, Leaf2, NamespaceId, NodeState,
17 NsProof, Payload, PrivKey, PubKey, RegisteredValidatorMap, SeqTypes, StakeTableHash,
18 StakeTableState, Transaction,
19 v0_3::{AuthenticatedValidator, RegisteredValidator, StakeTableEvent},
20};
21use hotshot_contract_adapter::sol_types::StakeTableV2::{Delegated, ValidatorRegistered};
22use hotshot_query_service::{
23 availability::{LeafHash, LeafId, LeafQueryData},
24 node::{BlockHash, BlockId},
25};
26use hotshot_types::{
27 data::{
28 EpochNumber, QuorumProposal2, QuorumProposalWrapper, VidCommitment, VidCommon, ViewNumber,
29 vid_commitment,
30 },
31 message::UpgradeLock,
32 signature_key::SchnorrPubKey,
33 simple_certificate::{NextEpochQuorumCertificate2, QuorumCertificate2, UpgradeCertificate},
34 simple_vote::{NextEpochQuorumData2, QuorumData2, UpgradeProposalData, VersionedVoteData},
35 stake_table::{StakeTableEntry, supermajority_threshold},
36 traits::{
37 block_contents::EncodeBytes,
38 signature_key::{SignatureKey, StateSignatureKey},
39 },
40 utils::{epoch_from_block_number, is_epoch_transition, is_ge_epoch_root},
41 vid::avidm::init_avidm_param,
42 vote::Certificate as _,
43};
44use jf_merkle_tree_compat::{
45 AppendableMerkleTreeScheme, MerkleTreeScheme, prelude::SHA3MerkleTree,
46};
47use rand::RngCore;
48use vbs::version::{StaticVersionType, Version};
49use versions::{DRB_AND_HEADER_UPGRADE_VERSION, EPOCH_VERSION, FEE_VERSION, Upgrade, version};
50
51use crate::{
52 client::Client,
53 consensus::{
54 header::HeaderProof,
55 leaf::LeafProof,
56 namespace::NamespaceProof,
57 payload::PayloadProof,
58 quorum::{Certificate, Quorum},
59 },
60 state::Genesis,
61 storage::LeafRequest,
62};
63
64pub const ENABLE_EPOCHS: Upgrade = Upgrade::new(FEE_VERSION, DRB_AND_HEADER_UPGRADE_VERSION);
65
66pub const LEGACY_VERSION: Version = FEE_VERSION;
67
68pub fn qc_chain_from_leaf_chain<'a>(
73 leaves: impl IntoIterator<Item = &'a LeafQueryData<SeqTypes>>,
74) -> Vec<Certificate> {
75 leaves
76 .into_iter()
77 .map(|leaf| Certificate::for_parent(leaf.leaf()))
78 .collect()
79}
80
81pub async fn leaf_chain(
83 range: impl IntoIterator<Item = u64>,
84 base: Version,
85) -> Vec<LeafQueryData<SeqTypes>> {
86 custom_leaf_chain(Upgrade::trivial(base), range, |_| {}).await
87}
88
89pub async fn leaf_chain_with_upgrade(
93 range: impl IntoIterator<Item = u64>,
94 upgrade_height: u64,
95 upgrade: Upgrade,
96) -> Vec<LeafQueryData<SeqTypes>> {
97 custom_leaf_chain_with_upgrade(range, upgrade_height, upgrade, |_| {}).await
98}
99
100pub async fn custom_leaf_chain_with_upgrade(
104 range: impl IntoIterator<Item = u64>,
105 upgrade_height: u64,
106 upgrade: Upgrade,
107 map: impl Fn(&mut QuorumProposal2<SeqTypes>),
108) -> Vec<LeafQueryData<SeqTypes>> {
109 let upgrade_leaf: Leaf2 = Leaf2::genesis(
110 &Default::default(),
111 &NodeState::mock()
112 .with_genesis_version(upgrade.target)
113 .with_current_version(upgrade.target),
114 upgrade.base,
115 )
116 .await;
117 let upgrade_data = UpgradeProposalData {
118 old_version: upgrade.base,
119 new_version: upgrade.target,
120 new_version_hash: Default::default(),
121 old_version_last_view: ViewNumber::new(upgrade_height - 1),
122 new_version_first_view: ViewNumber::new(upgrade_height),
123 decide_by: ViewNumber::new(upgrade_height),
124 };
125 let upgrade_commit = upgrade_data.commit();
126 let upgrade_cert = UpgradeCertificate::new(
127 upgrade_data,
128 upgrade_commit,
129 ViewNumber::new(upgrade_height),
130 Default::default(),
131 Default::default(),
132 );
133
134 custom_leaf_chain(upgrade, range, |proposal| {
135 let height = proposal.block_header.height();
136 if height < upgrade_height {
137 proposal.upgrade_certificate = Some(upgrade_cert.clone());
139 } else {
140 proposal.upgrade_certificate = None;
143 proposal.block_header = upgrade_leaf.block_header().clone();
144 *proposal.block_header.height_mut() = height;
145 }
146 map(proposal);
147 })
148 .await
149}
150
151pub async fn custom_leaf_chain(
153 upgrade: Upgrade,
154 range: impl IntoIterator<Item = u64>,
155 map: impl Fn(&mut QuorumProposal2<SeqTypes>),
156) -> Vec<LeafQueryData<SeqTypes>> {
157 let node_state = NodeState::mock()
158 .with_genesis_version(upgrade.base)
159 .with_current_version(upgrade.base);
160 let genesis_leaf: Leaf2 = Leaf2::genesis(&Default::default(), &node_state, upgrade.base).await;
161 tracing::info!(?genesis_leaf, "leaf chain");
162
163 let mut qc = QuorumCertificate2::genesis(&Default::default(), &node_state, upgrade).await;
164 let mut quorum_proposal = QuorumProposalWrapper::<SeqTypes> {
165 proposal: QuorumProposal2::<SeqTypes> {
166 epoch: None,
167 block_header: genesis_leaf.block_header().clone(),
168 view_number: genesis_leaf.view_number(),
169 justify_qc: qc.clone(),
170 upgrade_certificate: None,
171 view_change_evidence: None,
172 next_drb_result: None,
173 next_epoch_justify_qc: None,
174 state_cert: None,
175 },
176 };
177
178 let mut block_merkle_tree = BlockMerkleTree::new(BLOCK_MERKLE_TREE_HEIGHT);
179 let mut leaves = vec![];
180 for height in range {
181 *quorum_proposal.proposal.block_header.height_mut() = height;
182 *quorum_proposal
183 .proposal
184 .block_header
185 .block_merkle_tree_root_mut() = block_merkle_tree.commitment();
186 quorum_proposal.proposal.view_number = ViewNumber::new(height);
187 map(&mut quorum_proposal.proposal);
188 let leaf = Leaf2::from_quorum_proposal(&quorum_proposal);
189
190 qc.view_number = ViewNumber::new(height);
191 qc.data.leaf_commit = Committable::commit(&leaf);
192 if leaf.block_header().version() >= EPOCH_VERSION {
193 qc.data.block_number = Some(height);
194 }
195
196 block_merkle_tree
197 .push(leaf.block_header().commit())
198 .unwrap();
199 leaves.push(LeafQueryData::new(leaf, qc.clone()).unwrap());
200 quorum_proposal.proposal.justify_qc = qc.clone();
201 }
202
203 leaves
204}
205
206pub async fn epoch_change_leaf_chain(
208 range: impl IntoIterator<Item = u64>,
209 epoch_height: u64,
210 version: Version,
211) -> Vec<LeafQueryData<SeqTypes>> {
212 custom_epoch_change_leaf_chain(range, epoch_height, version, |_| {}).await
213}
214
215pub async fn custom_epoch_change_leaf_chain(
217 range: impl IntoIterator<Item = u64>,
218 epoch_height: u64,
219 version: Version,
220 map: impl Fn(&mut QuorumProposal2<SeqTypes>),
221) -> Vec<LeafQueryData<SeqTypes>> {
222 custom_leaf_chain(Upgrade::trivial(version), range, |proposal| {
223 if is_epoch_transition(proposal.block_header.height(), epoch_height) {
224 let data: NextEpochQuorumData2<SeqTypes> = proposal.justify_qc.data.clone().into();
225 let commit = data.commit();
226 proposal.next_epoch_justify_qc = Some(NextEpochQuorumCertificate2::new(
227 data,
228 commit,
229 proposal.justify_qc.view_number,
230 Default::default(),
231 Default::default(),
232 ));
233 map(proposal);
234 }
235 })
236 .await
237}
238
239#[derive(Clone, Copy, Debug, Default)]
240pub struct AlwaysTrueQuorum;
241
242impl Quorum for AlwaysTrueQuorum {
243 async fn verify_static<V: StaticVersionType + 'static>(&self, _: &Certificate) -> Result<()> {
244 Ok(())
245 }
246}
247
248#[derive(Clone, Copy, Debug, Default)]
249pub struct AlwaysFalseQuorum;
250
251impl Quorum for AlwaysFalseQuorum {
252 async fn verify_static<V: StaticVersionType + 'static>(&self, _: &Certificate) -> Result<()> {
253 bail!("always false quorum");
254 }
255}
256
257#[derive(Clone, Debug, Default)]
260pub struct VersionCheckQuorum {
261 leaves: HashMap<Commitment<Leaf2>, Leaf2>,
262}
263
264impl VersionCheckQuorum {
265 pub fn new(leaves: impl IntoIterator<Item = Leaf2>) -> Self {
266 Self {
267 leaves: leaves
268 .into_iter()
269 .map(|leaf| (leaf.commit(), leaf))
270 .collect(),
271 }
272 }
273}
274
275impl Quorum for VersionCheckQuorum {
276 async fn verify_static<V: StaticVersionType + 'static>(
277 &self,
278 cert: &Certificate,
279 ) -> anyhow::Result<()> {
280 let leaf = self
281 .leaves
282 .get(&cert.leaf_commit())
283 .context(format!("unknown leaf {}", cert.leaf_commit()))?;
284 ensure!(
285 leaf.block_header().version() == version(V::MAJOR, V::MINOR),
286 "version mismatch: leaf has version {}, but verifier is using version {}",
287 leaf.block_header().version(),
288 V::version()
289 );
290 Ok(())
291 }
292}
293
294#[derive(Clone, Debug, Default)]
296pub struct EpochChangeQuorum {
297 epoch_height: u64,
298}
299
300impl EpochChangeQuorum {
301 pub fn new(epoch_height: u64) -> Self {
302 Self { epoch_height }
303 }
304}
305
306impl Quorum for EpochChangeQuorum {
307 async fn verify_static<V: StaticVersionType + 'static>(
308 &self,
309 cert: &Certificate,
310 ) -> anyhow::Result<()> {
311 if V::version() >= EpochVersion::version() {
312 cert.verify_next_epoch_qc(self.epoch_height)?;
313 }
314 Ok(())
315 }
316}
317
318#[derive(Clone, Debug)]
319pub struct TestClient {
320 inner: Arc<Mutex<InnerTestClient>>,
321 epoch_height: u64,
322}
323
324impl Default for TestClient {
325 fn default() -> Self {
326 Self {
327 inner: Default::default(),
328 epoch_height: 100,
329 }
330 }
331}
332
333#[derive(Debug, Derivative)]
334#[derivative(Default)]
335struct InnerTestClient {
336 leaves: Vec<LeafQueryData<SeqTypes>>,
337 payloads: Vec<Payload>,
338 merkle_trees: Vec<SHA3MerkleTree<BlockHash<SeqTypes>>>,
341 leaf_hashes: HashMap<LeafHash<SeqTypes>, usize>,
342 block_hashes: HashMap<BlockHash<SeqTypes>, usize>,
343 payload_hashes: HashMap<VidCommitment, usize>,
344 missing_leaves: HashSet<usize>,
345 invalid_proofs: HashSet<usize>,
346 swapped_leaves: HashMap<usize, usize>,
347 invalid_payloads: HashSet<usize>,
348 quorum: Vec<(PrivKey, AuthenticatedValidator<PubKey>)>,
349 #[derivative(Default(value = "3"))]
350 first_epoch_with_dynamic_stake_table: u64,
351 missing_quorums: HashSet<u64>,
352 invalid_quorums: HashSet<u64>,
353 mock_block_height: Option<u64>,
354}
355
356impl InnerTestClient {
357 fn quorum_for_epoch(&mut self, epoch: u64) -> &[(PrivKey, AuthenticatedValidator<PubKey>)] {
358 let quorum_size = max(epoch, self.first_epoch_with_dynamic_stake_table) as usize;
362
363 while self.quorum.len() < quorum_size {
364 let (stake_table_key, priv_key) =
365 PubKey::generated_from_seed_indexed(Default::default(), self.quorum.len() as u64);
366 let (state_ver_key, _) = SchnorrPubKey::generated_from_seed_indexed(
367 Default::default(),
368 self.quorum.len() as u64,
369 );
370 let stake = U256::from(self.quorum.len() + 1) * U256::from(1_000_000_000u128);
371 let validator: AuthenticatedValidator<PubKey> = RegisteredValidator {
372 account: Address::random(),
373 stake_table_key,
374 state_ver_key,
375 stake,
376 commission: 1,
377 delegators: [(Address::random(), stake)].into_iter().collect(),
378 authenticated: true,
379 x25519_key: None,
380 p2p_addr: None,
381 }
382 .try_into()
383 .expect("authenticated validator");
384 self.quorum.push((priv_key, validator));
385 }
386
387 &self.quorum[..quorum_size]
388 }
389
390 fn stake_table_hash(&mut self, epoch: u64) -> StakeTableHash {
391 let quorum = self.quorum_for_epoch(epoch);
392 let mut validators = RegisteredValidatorMap::default();
393 let mut used_bls_keys = HashSet::default();
394 let mut used_schnorr_keys = HashSet::default();
395 for (_, validator) in quorum {
396 validators.insert(validator.account, validator.clone().into());
397 used_bls_keys.insert(validator.stake_table_key);
398 used_schnorr_keys.insert(validator.state_ver_key.clone());
399 }
400
401 let state = StakeTableState::new(
402 validators,
403 Default::default(),
404 used_bls_keys,
405 used_schnorr_keys,
406 );
407 state.commit()
408 }
409
410 async fn leaf(&mut self, height: usize, epoch_height: u64) -> LeafQueryData<SeqTypes> {
411 let epoch = epoch_from_block_number(height as u64, epoch_height);
412 let (quorum, stake_table): (Vec<_>, Vec<_>) =
413 self.quorum_for_epoch(epoch).iter().cloned().unzip();
414 let mut stake_entries = vec![];
415 let mut total_stake = U256::ZERO;
416 for validator in &stake_table {
417 stake_entries.push(StakeTableEntry {
418 stake_key: validator.stake_table_key,
419 stake_amount: validator.stake,
420 });
421 total_stake += validator.stake;
422 }
423
424 let pp = PubKey::public_parameter(&stake_entries, supermajority_threshold(total_stake));
425
426 for i in self.leaves.len()..=height {
427 let epoch = EpochNumber::new(epoch_from_block_number(i as u64, epoch_height));
428 let view_number = ViewNumber::new(i as u64);
429
430 let upgrade = Upgrade::trivial(DRB_AND_HEADER_UPGRADE_VERSION);
431 let node_state = NodeState::mock_v3().with_genesis_version(upgrade.base);
432 let (justify_qc, mt) = if i == 0 {
433 (
434 QuorumCertificate2::genesis(&Default::default(), &node_state, upgrade).await,
435 SHA3MerkleTree::new(BLOCK_MERKLE_TREE_HEIGHT),
436 )
437 } else {
438 let parent = &self.leaves[i - 1];
439 let mut mt = self.merkle_trees[i - 1].clone();
440 mt.push(parent.block_hash()).unwrap();
441 (parent.qc().clone(), mt)
442 };
443
444 let transactions = vec![Transaction::random(&mut rand::thread_rng())];
445 let (payload, ns_table) =
446 Payload::from_transactions_sync(transactions, node_state.chain_config).unwrap();
447 let payload_comm = vid_commitment(
448 &payload.encode(),
449 &ns_table.encode(),
450 quorum.len(),
451 upgrade.base,
452 );
453
454 let mut block_header = Leaf2::genesis(&Default::default(), &node_state, upgrade.base)
455 .await
456 .block_header()
457 .clone();
458 *block_header.height_mut() = i as u64;
459 *block_header.block_merkle_tree_root_mut() = mt.commitment();
460 *block_header.payload_commitment_mut() = payload_comm;
461 *block_header.ns_table_mut() = ns_table;
462 if *epoch + 1 >= self.first_epoch_with_dynamic_stake_table
463 && is_ge_epoch_root(block_header.height(), epoch_height)
464 {
465 assert!(block_header.set_next_stake_table_hash(self.stake_table_hash(*epoch + 1)));
466 }
467 let quorum_proposal = QuorumProposalWrapper::<SeqTypes> {
468 proposal: QuorumProposal2::<SeqTypes> {
469 epoch: Some(epoch),
470 block_header,
471 view_number,
472 justify_qc,
473 upgrade_certificate: None,
474 view_change_evidence: None,
475 next_drb_result: None,
476 next_epoch_justify_qc: None,
477 state_cert: None,
478 },
479 };
480 let leaf = Leaf2::from_quorum_proposal(&quorum_proposal);
481 let quorum_data = QuorumData2 {
482 leaf_commit: leaf.commit(),
483 epoch: Some(epoch),
484 block_number: Some(i as u64),
485 };
486 let quorum_data_comm = VersionedVoteData::new_infallible(
487 quorum_data.clone(),
488 view_number,
489 &UpgradeLock::<SeqTypes>::new(upgrade),
490 )
491 .commit();
492 let signatures = quorum
493 .iter()
494 .map(|key| PubKey::sign(key, quorum_data_comm.as_ref()).unwrap())
495 .collect::<Vec<_>>();
496 let assembled = PubKey::assemble(
497 &pp,
498 &std::iter::repeat_n(true, quorum.len()).collect::<BitVec>(),
499 &signatures,
500 );
501 let qc = QuorumCertificate2::create_signed_certificate(
502 quorum_data_comm,
503 quorum_data,
504 assembled,
505 view_number,
506 );
507 let leaf = LeafQueryData::new(leaf, qc).unwrap();
508 self.leaf_hashes.insert(leaf.hash(), i);
509 self.block_hashes.insert(leaf.block_hash(), i);
510 self.payload_hashes.entry(leaf.payload_hash()).or_insert(i);
511 self.leaves.push(leaf);
512 self.payloads.push(payload);
513 self.merkle_trees.push(mt);
514 }
515
516 self.leaves[height].clone()
517 }
518
519 fn leaf_height(&self, req: LeafRequest) -> Result<usize> {
520 match req {
521 LeafRequest::Leaf(LeafId::Number(h)) | LeafRequest::Header(BlockId::Number(h)) => Ok(h),
522 LeafRequest::Leaf(LeafId::Hash(h)) => self
523 .leaf_hashes
524 .get(&h)
525 .copied()
526 .context(format!("missing leaf {h}")),
527 LeafRequest::Header(BlockId::Hash(h)) => self
528 .block_hashes
529 .get(&h)
530 .copied()
531 .context(format!("missing block {h}")),
532 LeafRequest::Header(BlockId::PayloadHash(h)) => self
533 .payload_hashes
534 .get(&h)
535 .copied()
536 .context(format!("missing payload {h}")),
537 }
538 }
539
540 fn vid_common(&mut self, height: u64, epoch_height: u64) -> VidCommon {
541 let epoch = epoch_from_block_number(height, epoch_height);
542 let quorum = self.quorum_for_epoch(epoch);
543 VidCommon::V1(init_avidm_param(quorum.len()).unwrap())
544 }
545}
546
547impl TestClient {
548 pub async fn genesis(&self) -> Genesis {
549 let mut inner = self.inner.lock().await;
550 Genesis {
551 epoch_height: self.epoch_height,
552 stake_table: inner
553 .quorum_for_epoch(1)
554 .iter()
555 .map(|(_, validator)| StakeTableEntry {
556 stake_key: validator.stake_table_key,
557 stake_amount: validator.stake,
558 })
559 .collect(),
560 first_epoch_with_dynamic_stake_table: EpochNumber::new(
561 inner.first_epoch_with_dynamic_stake_table,
562 ),
563
564 #[cfg(feature = "decaf")]
565 decaf_first_pos_epoch: None,
566 }
567 }
568
569 pub async fn leaf(&self, height: usize) -> LeafQueryData<SeqTypes> {
570 let mut inner = self.inner.lock().await;
571 inner.leaf(height, self.epoch_height).await
572 }
573
574 pub async fn payload(&self, height: usize) -> Payload {
575 let mut inner = self.inner.lock().await;
576 inner.leaf(height, self.epoch_height).await;
577 inner.payloads[height].clone()
578 }
579
580 pub async fn return_invalid_payload(&self, for_height: usize) {
581 let mut inner = self.inner.lock().await;
582 inner.invalid_payloads.insert(for_height);
583 }
584
585 pub async fn remember_leaf(&self, height: usize) -> LeafQueryData<SeqTypes> {
586 let mut inner = self.inner.lock().await;
587 inner.missing_leaves.remove(&height);
588 inner.invalid_proofs.remove(&height);
589 inner.swapped_leaves.remove(&height);
590 inner.leaf(height, self.epoch_height).await
591 }
592
593 pub async fn forget_leaf(&self, height: usize) -> LeafQueryData<SeqTypes> {
594 let mut inner = self.inner.lock().await;
595 inner.missing_leaves.insert(height);
596 inner.leaf(height, self.epoch_height).await
597 }
598
599 pub async fn return_invalid_proof(&self, for_height: usize) {
600 let mut inner = self.inner.lock().await;
601 inner.invalid_proofs.insert(for_height);
602 }
603
604 pub async fn return_wrong_leaf(&self, for_height: usize, substitute: usize) {
605 let mut inner = self.inner.lock().await;
606 inner.swapped_leaves.insert(for_height, substitute);
607 }
608
609 pub async fn quorum_for_epoch(&self, epoch: EpochNumber) -> Vec<StakeTableEntry<PubKey>> {
610 let mut inner = self.inner.lock().await;
611 inner
612 .quorum_for_epoch(*epoch)
613 .iter()
614 .map(|(_, validator)| StakeTableEntry {
615 stake_key: validator.stake_table_key,
616 stake_amount: validator.stake,
617 })
618 .collect()
619 }
620
621 pub async fn remember_quorum(&self, epoch: EpochNumber) {
622 let mut inner = self.inner.lock().await;
623 inner.missing_quorums.remove(&*epoch);
624 }
625
626 pub async fn forget_quorum(&self, epoch: EpochNumber) {
627 let mut inner = self.inner.lock().await;
628 inner.missing_quorums.insert(*epoch);
629 }
630
631 pub async fn return_invalid_quorum(&self, epoch: EpochNumber) {
632 let mut inner = self.inner.lock().await;
633 inner.invalid_quorums.insert(*epoch);
634 }
635
636 pub async fn vid_common(&self, height: u64) -> VidCommon {
637 let mut inner = self.inner.lock().await;
638 inner.vid_common(height, self.epoch_height)
639 }
640
641 pub async fn mock_block_height(&self, height: u64) {
642 let mut inner = self.inner.lock().await;
643 inner.mock_block_height = Some(height);
644 }
645}
646
647impl Client for TestClient {
648 async fn block_height(&self) -> Result<u64> {
649 let inner = self.inner.lock().await;
650 Ok(inner
651 .mock_block_height
652 .unwrap_or_else(|| inner.leaves.len() as u64))
653 }
654
655 async fn leaf_proof(
656 &self,
657 id: impl Into<LeafRequest> + Send,
658 finalized: Option<u64>,
659 ) -> Result<LeafProof> {
660 let mut inner = self.inner.lock().await;
661
662 let mut height = inner.leaf_height(id.into())?;
663 ensure!(
664 !inner.missing_leaves.contains(&height),
665 "missing leaf {height}"
666 );
667 if inner.invalid_proofs.contains(&height) {
668 tracing::info!(height, "return mock incorrect proof");
669 return Ok(LeafProof::default());
670 }
671 if let Some(sub) = inner.swapped_leaves.get(&height) {
672 tracing::info!(height, sub, "return wrong leaf");
673 height = *sub;
674 };
675
676 let leaf = inner.leaf(height, self.epoch_height).await;
677
678 let mut proof = LeafProof::default();
679 proof.push(leaf);
680 if let Some(finalized) = finalized {
681 ensure!(
682 finalized > (height as u64),
683 "assumed finalized leaf must be after requested leaf"
684 );
685 if finalized <= (height as u64) + 2 {
686 tracing::info!(
687 height,
688 finalized,
689 "path to finalized is shorter than path to QC-chain, using finalized"
690 );
691 return Ok(proof);
692 }
693 }
694
695 proof.push(inner.leaf(height + 1, self.epoch_height).await);
696 assert!(proof.push(inner.leaf(height + 2, self.epoch_height).await));
697
698 Ok(proof)
699 }
700
701 async fn header_proof(&self, root: u64, id: BlockId<SeqTypes>) -> Result<HeaderProof> {
702 let mut inner = self.inner.lock().await;
703
704 let root = root as usize;
705 let mut height = inner.leaf_height(id.into())?;
706 ensure!(
707 !inner.missing_leaves.contains(&height),
708 "missing leaf {height}"
709 );
710 if inner.invalid_proofs.contains(&height) {
711 tracing::info!(root, height, "return mock invalid proof");
712 let leaf = inner.leaf(height, self.epoch_height).await;
713 let mt = BlockMerkleTree::from_elems(
715 Some(BLOCK_MERKLE_TREE_HEIGHT + 1),
716 [leaf.block_hash()],
717 )
718 .unwrap();
719 let proof = mt.lookup(0).expect_ok().unwrap().1;
720 return Ok(HeaderProof::new(leaf.header().clone(), proof));
721 }
722 if let Some(sub) = inner.swapped_leaves.get(&height) {
723 tracing::info!(height, sub, "return wrong leaf");
724 height = *sub;
725 };
726
727 ensure!(height < root);
728
729 let mt = &inner.merkle_trees[root];
730 tracing::info!(height, root = %mt.commitment(), "get proof from Merkle tree");
731 let proof = mt.lookup(height as u64).expect_ok().unwrap().1;
732 let header = inner.leaf(height, self.epoch_height).await.header().clone();
733 Ok(HeaderProof::new(header, proof))
734 }
735
736 async fn get_leaves_in_range(
737 &self,
738 start_height: usize,
739 end_height: usize,
740 ) -> Result<Vec<LeafQueryData<SeqTypes>>> {
741 let mut leaves = Vec::new();
742 let mut inner = self.inner.lock().await;
743 for h in start_height..end_height {
744 let height = *inner.swapped_leaves.get(&h).unwrap_or(&h);
745 leaves.push(inner.leaf(height, self.epoch_height).await);
746 }
747 Ok(leaves)
748 }
749
750 async fn stake_table_events(&self, epoch: EpochNumber) -> Result<Vec<StakeTableEvent>> {
751 let mut inner = self.inner.lock().await;
752
753 ensure!(
754 !inner.missing_quorums.contains(&*epoch),
755 "missing quorum for epoch {epoch}"
756 );
757
758 if inner.invalid_quorums.contains(&*epoch) {
759 let mut events = vec![];
761 let mut seed = [0; 32];
762 rand::thread_rng().fill_bytes(&mut seed);
763 register_validator_events(&mut events, &random_validator());
764 return Ok(events);
765 }
766
767 let mut events = vec![];
768 if *epoch == inner.first_epoch_with_dynamic_stake_table {
769 for (_, validator) in inner.quorum_for_epoch(*epoch) {
772 register_validator_events(&mut events, validator);
773 }
774 } else if *epoch > inner.first_epoch_with_dynamic_stake_table {
775 let (_, validator) = &inner.quorum_for_epoch(*epoch)[(*epoch as usize) - 1];
778 register_validator_events(&mut events, validator);
779 }
780 Ok(events)
781 }
782
783 async fn payload_proof(&self, height: u64) -> Result<PayloadProof> {
784 let mut inner = self.inner.lock().await;
785
786 let vid_common = inner.vid_common(height, self.epoch_height);
787 let height = height as usize;
788 ensure!(height < inner.payloads.len());
789
790 let payload = if inner.invalid_payloads.contains(&height) {
791 tracing::info!(height, "return mock incorrect payload proof");
792 Payload::from_transactions_sync(
793 [Transaction::random(&mut rand::thread_rng())],
794 NodeState::mock_v3().chain_config,
795 )
796 .unwrap()
797 .0
798 } else {
799 inner.payloads[height].clone()
800 };
801
802 Ok(PayloadProof::new(payload, vid_common))
803 }
804
805 async fn namespace_proof(
806 &self,
807 height: u64,
808 mut namespace: NamespaceId,
809 ) -> Result<NamespaceProof> {
810 let mut inner = self.inner.lock().await;
811
812 let vid_common = inner.vid_common(height, self.epoch_height);
813 let height = height as usize;
814 ensure!(height < inner.payloads.len());
815
816 let (payload, ns_table) = if inner.invalid_payloads.contains(&height) {
817 tracing::info!(height, "return mock incorrect namespace proof");
820 let mut payload = vec![0; 32];
821 rand::thread_rng().fill_bytes(&mut payload);
822 let tx = Transaction::new(namespace, payload);
823 let node_state = NodeState::mock_v3();
824 Payload::from_transactions_sync([tx], node_state.chain_config).unwrap()
825 } else {
826 (
827 inner.payloads[height].clone(),
828 inner.leaves[height].header().ns_table().clone(),
829 )
830 };
831
832 if inner.invalid_proofs.contains(&height) {
833 namespace = NamespaceId::from(u64::from(namespace) + 1);
836 }
837
838 let Some(ns_index) = ns_table.find_ns_id(&namespace) else {
839 return Ok(NamespaceProof::not_present());
840 };
841 let proof = NsProof::new(&payload, &ns_index, &vid_common)
842 .context("failed to construct NsProof")?;
843 Ok(NamespaceProof::new(proof, vid_common))
844 }
845
846 async fn namespace_proofs_in_range(
847 &self,
848 start: u64,
849 end: u64,
850 namespace: NamespaceId,
851 ) -> Result<Vec<NamespaceProof>> {
852 let mut proofs = vec![];
853 for i in start..end {
854 proofs.push(self.namespace_proof(i, namespace).await?);
855 }
856 Ok(proofs)
857 }
858}
859
860fn register_validator_events(
861 events: &mut Vec<StakeTableEvent>,
862 validator: &AuthenticatedValidator<PubKey>,
863) {
864 events.push(StakeTableEvent::Register(ValidatorRegistered {
865 account: validator.account,
866 blsVk: validator.stake_table_key.into(),
867 schnorrVk: validator.state_ver_key.clone().into(),
868 commission: validator.commission,
869 }));
870 for (&delegator, &amount) in &validator.delegators {
871 events.push(StakeTableEvent::Delegate(Delegated {
872 delegator,
873 validator: validator.account,
874 amount,
875 }));
876 }
877}
878
879pub fn random_validator() -> AuthenticatedValidator<PubKey> {
880 let account = Address::random();
881 let mut seed = [0; 32];
882 rand::thread_rng().fill_bytes(&mut seed);
883 let stake = U256::from(rand::thread_rng().next_u64());
884 RegisteredValidator {
885 account,
886 stake_table_key: PubKey::generated_from_seed_indexed(seed, 0).0,
887 state_ver_key: SchnorrPubKey::generated_from_seed_indexed(seed, 0).0,
888 stake,
889 commission: 1,
890 delegators: [(Address::random(), stake)].into_iter().collect(),
891 authenticated: true,
892 x25519_key: None,
893 p2p_addr: None,
894 }
895 .try_into()
896 .expect("authenticated validator")
897}