1use std::{
8 cmp::max,
9 marker::PhantomData,
10 pin::Pin,
11 sync::Arc,
12 task::{Context, Poll},
13};
14
15use committable::Committable;
16use futures::{future::BoxFuture, FutureExt, Stream};
17use hotshot::types::{BLSPubKey, SignatureKey, SystemContextHandle};
18use hotshot_example_types::{
19 block_types::{TestBlockHeader, TestBlockPayload, TestTransaction},
20 node_types::{MemoryImpl, TestTypes, TestVersions},
21 state_types::{TestInstanceState, TestValidatedState},
22};
23use hotshot_types::{
24 data::{
25 DaProposal2, EpochNumber, Leaf2, QuorumProposal2, QuorumProposalWrapper, VidDisperse,
26 VidDisperseShare, ViewChangeEvidence2, ViewNumber,
27 },
28 epoch_membership::{EpochMembership, EpochMembershipCoordinator},
29 message::{Proposal, UpgradeLock},
30 simple_certificate::{
31 DaCertificate2, QuorumCertificate2, TimeoutCertificate2, UpgradeCertificate,
32 ViewSyncFinalizeCertificate2,
33 },
34 simple_vote::{
35 DaData2, DaVote2, QuorumData2, QuorumVote2, TimeoutData2, TimeoutVote2,
36 UpgradeProposalData, UpgradeVote, ViewSyncFinalizeData2, ViewSyncFinalizeVote2,
37 },
38 traits::{
39 consensus_api::ConsensusApi,
40 node_implementation::{ConsensusTime, NodeType, Versions},
41 BlockPayload,
42 },
43 utils::{genesis_epoch_from_version, EpochTransitionIndicator},
44};
45use rand::{thread_rng, Rng};
46use sha2::{Digest, Sha256};
47
48use crate::helpers::{
49 build_cert, build_da_certificate, build_vid_proposal, da_payload_commitment, TestNodeKeyMap,
50};
51
52#[derive(Clone)]
53pub struct TestView {
54 pub da_proposal: Proposal<TestTypes, DaProposal2<TestTypes>>,
55 pub quorum_proposal: Proposal<TestTypes, QuorumProposalWrapper<TestTypes>>,
56 pub leaf: Leaf2<TestTypes>,
57 pub view_number: ViewNumber,
58 pub epoch_number: Option<EpochNumber>,
59 pub membership: EpochMembershipCoordinator<TestTypes>,
60 pub node_key_map: Arc<TestNodeKeyMap>,
61 pub vid_disperse: Proposal<TestTypes, VidDisperse<TestTypes>>,
62 pub vid_proposal: (
63 Vec<Proposal<TestTypes, VidDisperseShare<TestTypes>>>,
64 <TestTypes as NodeType>::SignatureKey,
65 ),
66 pub leader_public_key: <TestTypes as NodeType>::SignatureKey,
67 pub da_certificate: DaCertificate2<TestTypes>,
68 pub transactions: Vec<TestTransaction>,
69 upgrade_data: Option<UpgradeProposalData<TestTypes>>,
70 formed_upgrade_certificate: Option<UpgradeCertificate<TestTypes>>,
71 view_sync_finalize_data: Option<ViewSyncFinalizeData2<TestTypes>>,
72 timeout_cert_data: Option<TimeoutData2<TestTypes>>,
73 upgrade_lock: UpgradeLock<TestTypes, TestVersions>,
74}
75
76impl TestView {
77 async fn find_leader_key_pair(
78 membership: &EpochMembership<TestTypes>,
79 node_key_map: &Arc<TestNodeKeyMap>,
80 view_number: <TestTypes as NodeType>::View,
81 ) -> (
82 <<TestTypes as NodeType>::SignatureKey as SignatureKey>::PrivateKey,
83 <TestTypes as NodeType>::SignatureKey,
84 ) {
85 let leader = membership
86 .leader(view_number)
87 .await
88 .expect("expected Membership::leader to succeed");
89
90 let sk = node_key_map
91 .get(&leader)
92 .expect("expected Membership::leader public key to be in node_key_map");
93
94 (sk.clone(), leader)
95 }
96
97 pub async fn genesis<V: Versions>(
98 membership: &EpochMembershipCoordinator<TestTypes>,
99 node_key_map: Arc<TestNodeKeyMap>,
100 ) -> Self {
101 let genesis_view = ViewNumber::new(1);
102 let genesis_epoch = genesis_epoch_from_version::<V, TestTypes>();
103 let upgrade_lock = UpgradeLock::new();
104
105 let transactions = Vec::new();
106
107 let (block_payload, metadata) =
108 <TestBlockPayload as BlockPayload<TestTypes>>::from_transactions(
109 transactions.clone(),
110 &TestValidatedState::default(),
111 &TestInstanceState::default(),
112 )
113 .await
114 .unwrap();
115
116 let builder_commitment = <TestBlockPayload as BlockPayload<TestTypes>>::builder_commitment(
117 &block_payload,
118 &metadata,
119 );
120 let epoch_membership = membership
121 .membership_for_epoch(genesis_epoch)
122 .await
123 .unwrap();
124 let (private_key, public_key) =
126 Self::find_leader_key_pair(&epoch_membership, &node_key_map, genesis_view).await;
127
128 let leader_public_key = public_key;
129
130 let genesis_version = upgrade_lock.version_infallible(genesis_view).await;
131
132 let payload_commitment = da_payload_commitment::<TestTypes, TestVersions>(
133 &epoch_membership,
134 transactions.clone(),
135 &metadata,
136 genesis_version,
137 )
138 .await;
139
140 let (vid_disperse, vid_proposal) = build_vid_proposal::<TestTypes, TestVersions>(
141 &epoch_membership,
142 genesis_view,
143 genesis_epoch,
144 &block_payload,
145 &metadata,
146 &private_key,
147 &upgrade_lock,
148 )
149 .await;
150
151 let da_certificate = build_da_certificate(
152 &epoch_membership,
153 genesis_view,
154 genesis_epoch,
155 transactions.clone(),
156 &metadata,
157 &public_key,
158 &private_key,
159 &upgrade_lock,
160 )
161 .await
162 .unwrap();
163
164 let block_header = TestBlockHeader::new(
165 &Leaf2::<TestTypes>::genesis::<V>(
166 &TestValidatedState::default(),
167 &TestInstanceState::default(),
168 )
169 .await,
170 payload_commitment,
171 builder_commitment,
172 metadata,
173 genesis_version,
174 );
175
176 let quorum_proposal_inner = QuorumProposalWrapper::<TestTypes> {
177 proposal: QuorumProposal2::<TestTypes> {
178 block_header: block_header.clone(),
179 view_number: genesis_view,
180 epoch: genesis_epoch,
181 justify_qc: QuorumCertificate2::genesis::<TestVersions>(
182 &TestValidatedState::default(),
183 &TestInstanceState::default(),
184 )
185 .await,
186 next_epoch_justify_qc: None,
187 upgrade_certificate: None,
188 view_change_evidence: None,
189 next_drb_result: None,
190 state_cert: None,
191 },
192 };
193
194 let encoded_transactions = Arc::from(TestTransaction::encode(&transactions));
195 let encoded_transactions_hash = Sha256::digest(&encoded_transactions);
196 let block_payload_signature =
197 <TestTypes as NodeType>::SignatureKey::sign(&private_key, &encoded_transactions_hash)
198 .expect("Failed to sign block payload");
199
200 let da_proposal_inner = DaProposal2::<TestTypes> {
201 encoded_transactions: encoded_transactions.clone(),
202 metadata,
203 view_number: genesis_view,
204 epoch: genesis_epoch,
205 epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
206 };
207
208 let da_proposal = Proposal {
209 data: da_proposal_inner,
210 signature: block_payload_signature,
211 _pd: PhantomData,
212 };
213
214 let mut leaf = Leaf2::from_quorum_proposal(&quorum_proposal_inner);
215 leaf.fill_block_payload_unchecked(TestBlockPayload {
216 transactions: transactions.clone(),
217 });
218
219 let signature = <BLSPubKey as SignatureKey>::sign(&private_key, leaf.commit().as_ref())
220 .expect("Failed to sign leaf commitment!");
221
222 let quorum_proposal = Proposal {
223 data: quorum_proposal_inner,
224 signature,
225 _pd: PhantomData,
226 };
227
228 TestView {
229 quorum_proposal,
230 leaf,
231 view_number: genesis_view,
232 epoch_number: genesis_epoch,
233 membership: membership.clone(),
234 node_key_map,
235 vid_disperse,
236 vid_proposal: (vid_proposal, public_key),
237 da_certificate,
238 transactions,
239 leader_public_key,
240 upgrade_data: None,
241 formed_upgrade_certificate: None,
242 view_sync_finalize_data: None,
243 timeout_cert_data: None,
244 da_proposal,
245 upgrade_lock,
246 }
247 }
248
249 pub async fn next_view_from_ancestor(&self, ancestor: TestView) -> Self {
255 let old = ancestor;
256 let old_view = old.view_number;
257 let old_epoch = old.epoch_number;
258
259 let next_view = max(old_view, self.view_number) + 1;
262
263 let transactions = &self.transactions;
264
265 let quorum_data = QuorumData2 {
266 leaf_commit: old.leaf.commit(),
267 epoch: old_epoch,
268 block_number: old_epoch.is_some().then(|| old.leaf.height()),
269 };
270
271 let (old_private_key, old_public_key) = Self::find_leader_key_pair(
273 &self
274 .membership
275 .membership_for_epoch(old_epoch)
276 .await
277 .unwrap(),
278 &self.node_key_map,
279 old_view,
280 )
281 .await;
282
283 let (private_key, public_key) = Self::find_leader_key_pair(
285 &self
286 .membership
287 .membership_for_epoch(self.epoch_number)
288 .await
289 .unwrap(),
290 &self.node_key_map,
291 next_view,
292 )
293 .await;
294
295 let leader_public_key = public_key;
296
297 let (block_payload, metadata) =
298 <TestBlockPayload as BlockPayload<TestTypes>>::from_transactions(
299 transactions.clone(),
300 &TestValidatedState::default(),
301 &TestInstanceState::default(),
302 )
303 .await
304 .unwrap();
305 let builder_commitment = <TestBlockPayload as BlockPayload<TestTypes>>::builder_commitment(
306 &block_payload,
307 &metadata,
308 );
309
310 let version = self.upgrade_lock.version_infallible(next_view).await;
311 let membership = self
312 .membership
313 .membership_for_epoch(self.epoch_number)
314 .await
315 .unwrap();
316 let payload_commitment = da_payload_commitment::<TestTypes, TestVersions>(
317 &membership,
318 transactions.clone(),
319 &metadata,
320 version,
321 )
322 .await;
323
324 let (vid_disperse, vid_proposal) = build_vid_proposal::<TestTypes, TestVersions>(
325 &membership,
326 next_view,
327 self.epoch_number,
328 &block_payload,
329 &metadata,
330 &private_key,
331 &self.upgrade_lock,
332 )
333 .await;
334
335 let da_certificate = build_da_certificate::<TestTypes, TestVersions>(
336 &membership,
337 next_view,
338 self.epoch_number,
339 transactions.clone(),
340 &metadata,
341 &public_key,
342 &private_key,
343 &self.upgrade_lock,
344 )
345 .await
346 .unwrap();
347
348 let quorum_certificate = build_cert::<
349 TestTypes,
350 TestVersions,
351 QuorumData2<TestTypes>,
352 QuorumVote2<TestTypes>,
353 QuorumCertificate2<TestTypes>,
354 >(
355 quorum_data,
356 &membership,
357 old_view,
358 &old_public_key,
359 &old_private_key,
360 &self.upgrade_lock,
361 )
362 .await;
363
364 let upgrade_certificate = if let Some(ref data) = self.upgrade_data {
365 let cert = build_cert::<
366 TestTypes,
367 TestVersions,
368 UpgradeProposalData<TestTypes>,
369 UpgradeVote<TestTypes>,
370 UpgradeCertificate<TestTypes>,
371 >(
372 data.clone(),
373 &membership,
374 next_view,
375 &public_key,
376 &private_key,
377 &self.upgrade_lock,
378 )
379 .await;
380
381 Some(cert)
382 } else {
383 self.formed_upgrade_certificate.clone()
384 };
385
386 let view_sync_certificate = if let Some(ref data) = self.view_sync_finalize_data {
387 let cert = build_cert::<
388 TestTypes,
389 TestVersions,
390 ViewSyncFinalizeData2<TestTypes>,
391 ViewSyncFinalizeVote2<TestTypes>,
392 ViewSyncFinalizeCertificate2<TestTypes>,
393 >(
394 data.clone(),
395 &membership,
396 next_view,
397 &public_key,
398 &private_key,
399 &self.upgrade_lock,
400 )
401 .await;
402
403 Some(cert)
404 } else {
405 None
406 };
407
408 let timeout_certificate = if let Some(ref data) = self.timeout_cert_data {
409 let cert = build_cert::<
410 TestTypes,
411 TestVersions,
412 TimeoutData2<TestTypes>,
413 TimeoutVote2<TestTypes>,
414 TimeoutCertificate2<TestTypes>,
415 >(
416 data.clone(),
417 &membership,
418 next_view,
419 &public_key,
420 &private_key,
421 &self.upgrade_lock,
422 )
423 .await;
424
425 Some(cert)
426 } else {
427 None
428 };
429
430 let view_change_evidence = if let Some(tc) = timeout_certificate {
431 Some(ViewChangeEvidence2::Timeout(tc))
432 } else {
433 view_sync_certificate.map(ViewChangeEvidence2::ViewSync)
434 };
435
436 let random = thread_rng().gen_range(0..=u64::MAX);
437
438 let block_header = TestBlockHeader {
439 block_number: *next_view,
440 timestamp: *next_view,
441 timestamp_millis: *next_view * 1_000,
442 payload_commitment,
443 builder_commitment,
444 metadata,
445 random,
446 version,
447 };
448
449 let proposal = QuorumProposalWrapper::<TestTypes> {
450 proposal: QuorumProposal2::<TestTypes> {
451 block_header: block_header.clone(),
452 view_number: next_view,
453 epoch: old_epoch,
454 justify_qc: quorum_certificate.clone(),
455 next_epoch_justify_qc: None,
456 upgrade_certificate: upgrade_certificate.clone(),
457 view_change_evidence,
458 next_drb_result: None,
459 state_cert: None,
460 },
461 };
462
463 let mut leaf = Leaf2::from_quorum_proposal(&proposal);
464 leaf.fill_block_payload_unchecked(TestBlockPayload {
465 transactions: transactions.clone(),
466 });
467
468 let signature = <BLSPubKey as SignatureKey>::sign(&private_key, leaf.commit().as_ref())
469 .expect("Failed to sign leaf commitment.");
470
471 let quorum_proposal = Proposal {
472 data: proposal,
473 signature,
474 _pd: PhantomData,
475 };
476
477 let encoded_transactions = Arc::from(TestTransaction::encode(transactions));
478 let encoded_transactions_hash = Sha256::digest(&encoded_transactions);
479 let block_payload_signature =
480 <TestTypes as NodeType>::SignatureKey::sign(&private_key, &encoded_transactions_hash)
481 .expect("Failed to sign block payload");
482
483 let da_proposal_inner = DaProposal2::<TestTypes> {
484 encoded_transactions: encoded_transactions.clone(),
485 metadata,
486 view_number: next_view,
487 epoch: old_epoch,
488 epoch_transition_indicator: EpochTransitionIndicator::NotInTransition,
489 };
490
491 let da_proposal = Proposal {
492 data: da_proposal_inner,
493 signature: block_payload_signature,
494 _pd: PhantomData,
495 };
496
497 let upgrade_lock = UpgradeLock::new();
498
499 TestView {
500 quorum_proposal,
501 leaf,
502 view_number: next_view,
503 epoch_number: self.epoch_number,
504 membership: self.membership.clone(),
505 node_key_map: self.node_key_map.clone(),
506 vid_disperse,
507 vid_proposal: (vid_proposal, public_key),
508 da_certificate,
509 leader_public_key,
510 transactions: Vec::new(),
513 upgrade_data: None,
514 formed_upgrade_certificate: upgrade_certificate,
517 view_sync_finalize_data: None,
518 timeout_cert_data: None,
519 da_proposal,
520 upgrade_lock,
521 }
522 }
523
524 pub async fn next_view(&self) -> Self {
525 self.next_view_from_ancestor(self.clone()).await
526 }
527
528 pub async fn create_quorum_vote(
529 &self,
530 handle: &SystemContextHandle<TestTypes, MemoryImpl, TestVersions>,
531 ) -> QuorumVote2<TestTypes> {
532 QuorumVote2::<TestTypes>::create_signed_vote(
533 QuorumData2 {
534 leaf_commit: self.leaf.commit(),
535 epoch: self.epoch_number,
536 block_number: Some(self.leaf.height()),
537 },
538 self.view_number,
539 &handle.public_key(),
540 handle.private_key(),
541 &handle.hotshot.upgrade_lock,
542 )
543 .await
544 .expect("Failed to generate a signature on QuorumVote")
545 }
546
547 pub async fn create_upgrade_vote(
548 &self,
549 data: UpgradeProposalData<TestTypes>,
550 handle: &SystemContextHandle<TestTypes, MemoryImpl, TestVersions>,
551 ) -> UpgradeVote<TestTypes> {
552 UpgradeVote::<TestTypes>::create_signed_vote(
553 data,
554 self.view_number,
555 &handle.public_key(),
556 handle.private_key(),
557 &handle.hotshot.upgrade_lock,
558 )
559 .await
560 .expect("Failed to generate a signature on UpgradVote")
561 }
562
563 pub async fn create_da_vote(
564 &self,
565 data: DaData2<TestTypes>,
566 handle: &SystemContextHandle<TestTypes, MemoryImpl, TestVersions>,
567 ) -> DaVote2<TestTypes> {
568 DaVote2::create_signed_vote(
569 data,
570 self.view_number,
571 &handle.public_key(),
572 handle.private_key(),
573 &handle.hotshot.upgrade_lock,
574 )
575 .await
576 .expect("Failed to sign DaData")
577 }
578}
579
580pub struct TestViewGenerator<V: Versions> {
581 pub current_view: Option<TestView>,
582 pub membership: EpochMembershipCoordinator<TestTypes>,
583 pub node_key_map: Arc<TestNodeKeyMap>,
584 pub _pd: PhantomData<fn(V)>,
585 pub task: Option<BoxFuture<'static, TestView>>,
586}
587
588impl<V: Versions> TestViewGenerator<V> {
589 pub fn generate(
590 membership: EpochMembershipCoordinator<TestTypes>,
591 node_key_map: Arc<TestNodeKeyMap>,
592 ) -> Self {
593 TestViewGenerator {
594 current_view: None,
595 membership,
596 node_key_map,
597 _pd: PhantomData,
598 task: None,
599 }
600 }
601
602 pub fn add_upgrade(&mut self, upgrade_proposal_data: UpgradeProposalData<TestTypes>) {
603 if let Some(ref view) = self.current_view {
604 self.current_view = Some(TestView {
605 upgrade_data: Some(upgrade_proposal_data),
606 ..view.clone()
607 });
608 } else {
609 tracing::error!("Cannot attach upgrade proposal to the genesis view.");
610 }
611 }
612
613 pub fn add_transactions(&mut self, transactions: Vec<TestTransaction>) {
614 if let Some(ref view) = self.current_view {
615 self.current_view = Some(TestView {
616 transactions,
617 ..view.clone()
618 });
619 } else {
620 tracing::error!("Cannot attach transactions to the genesis view.");
621 }
622 }
623
624 pub fn add_view_sync_finalize(
625 &mut self,
626 view_sync_finalize_data: ViewSyncFinalizeData2<TestTypes>,
627 ) {
628 if let Some(ref view) = self.current_view {
629 self.current_view = Some(TestView {
630 view_sync_finalize_data: Some(view_sync_finalize_data),
631 ..view.clone()
632 });
633 } else {
634 tracing::error!("Cannot attach view sync finalize to the genesis view.");
635 }
636 }
637
638 pub fn add_timeout(&mut self, timeout_data: TimeoutData2<TestTypes>) {
639 if let Some(ref view) = self.current_view {
640 self.current_view = Some(TestView {
641 timeout_cert_data: Some(timeout_data),
642 ..view.clone()
643 });
644 } else {
645 tracing::error!("Cannot attach timeout cert to the genesis view.")
646 }
647 }
648
649 pub fn advance_view_number_by(&mut self, n: u64) {
652 if let Some(ref view) = self.current_view {
653 self.current_view = Some(TestView {
654 view_number: view.view_number + n,
655 ..view.clone()
656 })
657 } else {
658 tracing::error!("Cannot attach view sync finalize to the genesis view.");
659 }
660 }
661
662 pub async fn next_from_ancestor_view(&mut self, ancestor: TestView) {
663 if let Some(ref view) = self.current_view {
664 self.current_view = Some(view.next_view_from_ancestor(ancestor).await)
665 } else {
666 tracing::error!("Cannot attach ancestor to genesis view.");
667 }
668 }
669}
670
671impl<V: Versions> Stream for TestViewGenerator<V> {
672 type Item = TestView;
673
674 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
675 if self.task.is_none() {
676 let cur_view = self.current_view.clone();
677 self.task = Some(if let Some(view) = cur_view {
678 async move { TestView::next_view(&view).await }.boxed()
679 } else {
680 let epoch_membership = self.membership.clone();
681 let nkm = Arc::clone(&self.node_key_map);
682 async move { TestView::genesis::<V>(&epoch_membership, nkm).await }.boxed()
683 });
684 }
685
686 match self.task.as_mut().unwrap().as_mut().poll(cx) {
687 Poll::Ready(test_view) => {
688 self.current_view = Some(test_view.clone());
689 self.task = None;
690 Poll::Ready(Some(test_view))
691 },
692 Poll::Pending => Poll::Pending,
693 }
694 }
695}