1use std::{collections::BTreeMap, fmt::Debug, hash::Hash, marker::PhantomData, time::Duration};
19
20use alloy::primitives::U256;
21use hotshot_utils::anytrace::*;
22use jf_advz::{VidDisperse as JfVidDisperse, VidScheme};
23use serde::{Deserialize, Serialize};
24use tokio::{task::spawn_blocking, time::Instant};
25
26use super::ns_table::parse_ns_table;
27use crate::{
28 PeerConfig,
29 data::{EpochNumber, ViewNumber},
30 epoch_membership::{EpochMembership, EpochMembershipCoordinator},
31 message::Proposal,
32 simple_vote::HasEpoch,
33 traits::{
34 BlockPayload,
35 block_contents::EncodeBytes,
36 node_implementation::NodeType,
37 signature_key::{SignatureKey, StakeTableEntryType},
38 },
39 vid::{
40 advz::{ADVZCommitment, ADVZCommon, ADVZScheme, ADVZShare, advz_scheme},
41 avidm::{AvidMCommitment, AvidMCommon, AvidMScheme, AvidMShare, init_avidm_param},
42 avidm_gf2::{
43 AvidmGf2Commitment, AvidmGf2Common, AvidmGf2Scheme, AvidmGf2Share, init_avidm_gf2_param,
44 },
45 },
46 vote::HasViewNumber,
47};
48
49impl<NODE: NodeType> HasEpoch for ADVZDisperse<NODE> {
50 fn epoch(&self) -> Option<EpochNumber> {
51 self.epoch
52 }
53}
54
55impl<NODE: NodeType> HasEpoch for AvidMDisperse<NODE> {
56 fn epoch(&self) -> Option<EpochNumber> {
57 self.epoch
58 }
59}
60
61impl<NODE: NodeType> HasEpoch for AvidMDisperseShare<NODE> {
62 fn epoch(&self) -> Option<EpochNumber> {
63 self.epoch
64 }
65}
66
67impl<NODE: NodeType> HasEpoch for AvidmGf2Disperse<NODE> {
68 fn epoch(&self) -> Option<EpochNumber> {
69 self.epoch
70 }
71}
72
73impl<NODE: NodeType> HasEpoch for AvidmGf2DisperseShare<NODE> {
74 fn epoch(&self) -> Option<EpochNumber> {
75 self.epoch
76 }
77}
78
79#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
81pub struct ADVZDisperse<TYPES: NodeType> {
82 pub view_number: ViewNumber,
84 pub epoch: Option<EpochNumber>,
86 pub target_epoch: Option<EpochNumber>,
88 pub payload_commitment: ADVZCommitment,
90 pub shares: BTreeMap<TYPES::SignatureKey, ADVZShare>,
92 pub common: ADVZCommon,
94}
95
96impl<TYPES: NodeType> HasViewNumber for ADVZDisperse<TYPES> {
97 fn view_number(&self) -> ViewNumber {
98 self.view_number
99 }
100}
101
102impl<TYPES: NodeType> ADVZDisperse<TYPES> {
103 async fn from_membership(
107 view_number: ViewNumber,
108 mut vid_disperse: JfVidDisperse<ADVZScheme>,
109 membership: &EpochMembershipCoordinator<TYPES>,
110 target_epoch: Option<EpochNumber>,
111 data_epoch: Option<EpochNumber>,
112 ) -> Result<Self> {
113 let shares = membership
114 .stake_table_for_epoch(target_epoch)?
115 .stake_table()
116 .map(|entry| entry.stake_table_entry.public_key())
117 .map(|node| (node.clone(), vid_disperse.shares.remove(0)))
118 .collect();
119
120 Ok(Self {
121 view_number,
122 shares,
123 common: vid_disperse.common,
124 payload_commitment: vid_disperse.commit,
125 epoch: data_epoch,
126 target_epoch,
127 })
128 }
129
130 #[allow(clippy::panic)]
136 pub async fn calculate_vid_disperse(
137 payload: &TYPES::BlockPayload,
138 membership: &EpochMembershipCoordinator<TYPES>,
139 view: ViewNumber,
140 target_epoch: Option<EpochNumber>,
141 data_epoch: Option<EpochNumber>,
142 ) -> Result<(Self, Duration)> {
143 let num_nodes = membership
144 .stake_table_for_epoch(target_epoch)?
145 .total_nodes();
146
147 let txns = payload.encode();
148
149 let now = Instant::now();
150 let vid_disperse = spawn_blocking(move || advz_scheme(num_nodes).disperse(&txns))
151 .await
152 .wrap()
153 .context(error!("Join error"))?
154 .wrap()
155 .context(|err| error!("Failed to calculate VID disperse. Error: {err}"))?;
156 let advz_scheme_duration = now.elapsed();
157
158 Ok((
159 Self::from_membership(view, vid_disperse, membership, target_epoch, data_epoch).await?,
160 advz_scheme_duration,
161 ))
162 }
163
164 pub fn to_shares(self) -> Vec<ADVZDisperseShare<TYPES>> {
166 self.shares
167 .into_iter()
168 .map(|(recipient_key, share)| ADVZDisperseShare {
169 share,
170 recipient_key,
171 view_number: self.view_number,
172 common: self.common.clone(),
173 payload_commitment: self.payload_commitment,
174 })
175 .collect()
176 }
177
178 pub fn to_share_proposals(
180 self,
181 signature: &<<TYPES as NodeType>::SignatureKey as SignatureKey>::PureAssembledSignatureType,
182 ) -> Vec<Proposal<TYPES, ADVZDisperseShare<TYPES>>> {
183 self.shares
184 .into_iter()
185 .map(|(recipient_key, share)| Proposal {
186 data: ADVZDisperseShare {
187 share,
188 recipient_key,
189 view_number: self.view_number,
190 payload_commitment: self.payload_commitment,
191 common: self.common.clone(),
192 },
193 signature: signature.clone(),
194 _pd: PhantomData,
195 })
196 .collect()
197 }
198
199 pub fn payload_byte_len(&self) -> u32 {
201 ADVZScheme::get_payload_byte_len(&self.common)
202 }
203}
204
205#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
206pub struct ADVZDisperseShare<TYPES: NodeType> {
208 pub view_number: ViewNumber,
210 pub payload_commitment: ADVZCommitment,
212 pub share: ADVZShare,
214 pub common: ADVZCommon,
216 pub recipient_key: TYPES::SignatureKey,
218}
219
220impl<NODE: NodeType> HasEpoch for ADVZDisperseShare<NODE> {
221 fn epoch(&self) -> Option<EpochNumber> {
222 None
223 }
224}
225
226impl<TYPES: NodeType> HasViewNumber for ADVZDisperseShare<TYPES> {
227 fn view_number(&self) -> ViewNumber {
228 self.view_number
229 }
230}
231
232impl<TYPES: NodeType> ADVZDisperseShare<TYPES> {
233 pub fn to_proposal(
235 self,
236 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
237 ) -> Option<Proposal<TYPES, Self>> {
238 let Ok(signature) =
239 TYPES::SignatureKey::sign(private_key, self.payload_commitment.as_ref())
240 else {
241 tracing::error!("VID: failed to sign dispersal share payload");
242 return None;
243 };
244 Some(Proposal {
245 signature,
246 _pd: PhantomData,
247 data: self,
248 })
249 }
250
251 pub fn to_advz_disperse<'a, I>(mut it: I) -> Option<ADVZDisperse<TYPES>>
253 where
254 I: Iterator<Item = &'a Self>,
255 {
256 let first_vid_disperse_share = it.next()?.clone();
257 let mut share_map = BTreeMap::new();
258 share_map.insert(
259 first_vid_disperse_share.recipient_key,
260 first_vid_disperse_share.share,
261 );
262 let mut vid_disperse = ADVZDisperse {
263 view_number: first_vid_disperse_share.view_number,
264 epoch: None,
265 target_epoch: None,
266 payload_commitment: first_vid_disperse_share.payload_commitment,
267 common: first_vid_disperse_share.common,
268 shares: share_map,
269 };
270 it.for_each(|vid_disperse_share| {
271 vid_disperse.shares.insert(
272 vid_disperse_share.recipient_key.clone(),
273 vid_disperse_share.share.clone(),
274 );
275 });
276 Some(vid_disperse)
277 }
278
279 pub fn is_consistent(&self) -> bool {
281 ADVZScheme::is_consistent(&self.payload_commitment, &self.common).is_ok()
282 }
283
284 pub fn verify_with_verified_common(&self) -> bool {
287 let total_weight = ADVZScheme::get_num_storage_nodes(&self.common) as usize;
288 advz_scheme(total_weight)
289 .verify_share(&self.share, &self.common, &self.payload_commitment)
290 .is_ok_and(|r| r.is_ok())
291 }
292
293 pub fn verify(&self, _total_weight: usize) -> bool {
295 self.is_consistent() && self.verify_with_verified_common()
296 }
297
298 pub fn payload_byte_len(&self) -> u32 {
300 ADVZScheme::get_payload_byte_len(&self.common)
301 }
302}
303
304#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
306pub struct AvidMDisperse<TYPES: NodeType> {
307 pub view_number: ViewNumber,
309 pub epoch: Option<EpochNumber>,
311 pub target_epoch: Option<EpochNumber>,
313 pub payload_commitment: AvidMCommitment,
315 pub shares: BTreeMap<TYPES::SignatureKey, AvidMShare>,
317 pub payload_byte_len: usize,
319 pub common: AvidMCommon,
321}
322
323impl<TYPES: NodeType> HasViewNumber for AvidMDisperse<TYPES> {
324 fn view_number(&self) -> ViewNumber {
325 self.view_number
326 }
327}
328
329pub const VID_TARGET_TOTAL_STAKE: u32 = 1000;
331
332struct Weights {
334 weights: Vec<u32>,
336
337 total_weight: usize,
339}
340
341pub fn vid_total_weight<'a, T, I>(stake_table: I, epoch: Option<EpochNumber>) -> usize
342where
343 T: NodeType,
344 I: Iterator<Item = &'a PeerConfig<T>>,
345{
346 if epoch.is_none() {
347 stake_table
348 .fold(U256::ZERO, |acc, entry| {
349 acc + entry.stake_table_entry.stake()
350 })
351 .to::<usize>()
352 } else {
353 let stake_table = stake_table.cloned().collect::<Vec<_>>();
354 approximate_weights(&stake_table[..]).total_weight
355 }
356}
357
358fn approximate_weights<TYPES: NodeType>(stake_table: &[PeerConfig<TYPES>]) -> Weights {
359 let total_stake = stake_table.iter().fold(U256::ZERO, |acc, entry| {
360 acc + entry.stake_table_entry.stake()
361 });
362
363 let mut total_weight: usize = 0;
364
365 if total_stake <= U256::from(VID_TARGET_TOTAL_STAKE) {
367 let weights = stake_table
368 .iter()
369 .map(|entry| entry.stake_table_entry.stake().to::<u32>())
370 .collect();
371
372 total_weight = total_stake.to::<usize>();
374
375 Weights {
376 weights,
377 total_weight,
378 }
379 } else {
380 let weights = stake_table
381 .iter()
382 .map(|entry| {
383 let weight: U256 = ((entry.stake_table_entry.stake()
384 * U256::from(VID_TARGET_TOTAL_STAKE))
385 / total_stake)
386 + U256::ONE;
387
388 total_weight += weight.to::<usize>();
390
391 weight.to::<u32>()
394 })
395 .collect();
396
397 Weights {
398 weights,
399 total_weight,
400 }
401 }
402}
403
404impl<TYPES: NodeType> AvidMDisperse<TYPES> {
405 async fn from_membership(
409 view_number: ViewNumber,
410 commit: AvidMCommitment,
411 shares: &[AvidMShare],
412 common: AvidMCommon,
413 membership: &EpochMembership<TYPES>,
414 target_epoch: Option<EpochNumber>,
415 data_epoch: Option<EpochNumber>,
416 ) -> Result<Self> {
417 let payload_byte_len = shares[0].payload_byte_len();
418 let shares = membership
419 .coordinator
420 .stake_table_for_epoch(target_epoch)?
421 .stake_table()
422 .map(|entry| entry.stake_table_entry.public_key())
423 .zip(shares)
424 .map(|(node, share)| (node.clone(), share.clone()))
425 .collect();
426
427 Ok(Self {
428 view_number,
429 shares,
430 payload_commitment: commit,
431 epoch: data_epoch,
432 target_epoch,
433 payload_byte_len,
434 common,
435 })
436 }
437
438 #[allow(clippy::panic)]
444 #[allow(clippy::single_range_in_vec_init)]
445 pub async fn calculate_vid_disperse(
446 payload: &TYPES::BlockPayload,
447 membership: &EpochMembershipCoordinator<TYPES>,
448 view: ViewNumber,
449 target_epoch: Option<EpochNumber>,
450 data_epoch: Option<EpochNumber>,
451 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
452 ) -> Result<(Self, Duration)> {
453 let target_mem = membership.stake_table_for_epoch(target_epoch)?;
454 let stake_table: Vec<_> = target_mem.stake_table().cloned().collect();
455 let approximate_weights = approximate_weights(&stake_table);
456
457 let txns = payload.encode();
458 let num_txns = txns.len();
459
460 let avidm_param = init_avidm_param(approximate_weights.total_weight)?;
461 let common = avidm_param.clone();
462
463 let ns_table = parse_ns_table(num_txns, &metadata.encode());
464 let ns_table_clone = ns_table.clone();
465
466 let now = Instant::now();
467 let (commit, shares) = spawn_blocking(move || {
468 AvidMScheme::ns_disperse(
469 &avidm_param,
470 &approximate_weights.weights,
471 &txns,
472 ns_table_clone,
473 )
474 })
475 .await
476 .wrap()
477 .context(error!("Join error"))?
478 .wrap()
479 .context(|err| error!("Failed to calculate VID disperse. Error: {err}"))?;
480 let ns_disperse_duration = now.elapsed();
481
482 Ok((
483 Self::from_membership(
484 view,
485 commit,
486 &shares,
487 common,
488 &target_mem,
489 target_epoch,
490 data_epoch,
491 )
492 .await?,
493 ns_disperse_duration,
494 ))
495 }
496
497 pub fn to_shares(self) -> Vec<AvidMDisperseShare<TYPES>> {
499 self.shares
500 .into_iter()
501 .map(|(recipient_key, share)| AvidMDisperseShare {
502 share,
503 recipient_key,
504 view_number: self.view_number,
505 payload_commitment: self.payload_commitment,
506 epoch: self.epoch,
507 target_epoch: self.target_epoch,
508 common: self.common.clone(),
509 })
510 .collect()
511 }
512
513 pub fn to_share_proposals(
515 self,
516 signature: &<<TYPES as NodeType>::SignatureKey as SignatureKey>::PureAssembledSignatureType,
517 ) -> Vec<Proposal<TYPES, AvidMDisperseShare<TYPES>>> {
518 self.shares
519 .into_iter()
520 .map(|(recipient_key, share)| Proposal {
521 data: AvidMDisperseShare {
522 share,
523 recipient_key,
524 view_number: self.view_number,
525 payload_commitment: self.payload_commitment,
526 epoch: self.epoch,
527 target_epoch: self.target_epoch,
528 common: self.common.clone(),
529 },
530 signature: signature.clone(),
531 _pd: PhantomData,
532 })
533 .collect()
534 }
535
536 pub fn try_from_shares<'a, I>(mut it: I) -> Option<Self>
538 where
539 I: Iterator<Item = &'a AvidMDisperseShare<TYPES>>,
540 {
541 let first_vid_disperse_share = it.next()?.clone();
542 let payload_byte_len = first_vid_disperse_share.share.payload_byte_len();
543 let mut share_map = BTreeMap::new();
544 share_map.insert(
545 first_vid_disperse_share.recipient_key,
546 first_vid_disperse_share.share,
547 );
548 let mut vid_disperse = Self {
549 view_number: first_vid_disperse_share.view_number,
550 epoch: first_vid_disperse_share.epoch,
551 target_epoch: first_vid_disperse_share.target_epoch,
552 payload_commitment: first_vid_disperse_share.payload_commitment,
553 shares: share_map,
554 payload_byte_len,
555 common: first_vid_disperse_share.common,
556 };
557 it.for_each(|vid_disperse_share| {
558 vid_disperse.shares.insert(
559 vid_disperse_share.recipient_key.clone(),
560 vid_disperse_share.share.clone(),
561 );
562 });
563 Some(vid_disperse)
564 }
565
566 pub fn payload_byte_len(&self) -> u32 {
568 self.payload_byte_len as u32
569 }
570}
571
572#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
573pub struct AvidMDisperseShare<TYPES: NodeType> {
575 pub view_number: ViewNumber,
577 pub epoch: Option<EpochNumber>,
579 pub target_epoch: Option<EpochNumber>,
581 pub payload_commitment: AvidMCommitment,
583 pub share: AvidMShare,
585 pub recipient_key: TYPES::SignatureKey,
587 pub common: AvidMCommon,
589}
590
591impl<TYPES: NodeType> HasViewNumber for AvidMDisperseShare<TYPES> {
592 fn view_number(&self) -> ViewNumber {
593 self.view_number
594 }
595}
596
597impl<TYPES: NodeType> AvidMDisperseShare<TYPES> {
598 pub fn to_proposal(
600 self,
601 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
602 ) -> Option<Proposal<TYPES, Self>> {
603 let Ok(signature) =
604 TYPES::SignatureKey::sign(private_key, self.payload_commitment.as_ref())
605 else {
606 tracing::error!("VID: failed to sign dispersal share payload");
607 return None;
608 };
609 Some(Proposal {
610 signature,
611 _pd: PhantomData,
612 data: self,
613 })
614 }
615
616 pub fn payload_byte_len(&self) -> u32 {
618 self.share.payload_byte_len() as u32
619 }
620
621 pub fn is_consistent(&self) -> bool {
624 true
625 }
626
627 pub fn verify_with_verified_common(&self) -> bool {
631 AvidMScheme::verify_share(&self.common, &self.payload_commitment, &self.share)
632 .is_ok_and(|r| r.is_ok())
633 }
634
635 pub fn verify(&self, _total_weight: usize) -> bool {
637 self.is_consistent() && self.verify_with_verified_common()
638 }
639}
640
641#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
643pub struct AvidmGf2Disperse<TYPES: NodeType> {
644 pub view_number: ViewNumber,
646 pub epoch: Option<EpochNumber>,
648 pub target_epoch: Option<EpochNumber>,
650 pub payload_commitment: AvidmGf2Commitment,
652 pub shares: BTreeMap<TYPES::SignatureKey, AvidmGf2Share>,
654 pub payload_byte_len: usize,
656 pub common: AvidmGf2Common,
658}
659
660impl<TYPES: NodeType> HasViewNumber for AvidmGf2Disperse<TYPES> {
661 fn view_number(&self) -> ViewNumber {
662 self.view_number
663 }
664}
665
666impl<TYPES: NodeType> AvidmGf2Disperse<TYPES> {
667 async fn from_membership(
671 view_number: ViewNumber,
672 commit: AvidmGf2Commitment,
673 shares: &[AvidmGf2Share],
674 common: AvidmGf2Common,
675 membership: &EpochMembership<TYPES>,
676 target_epoch: Option<EpochNumber>,
677 data_epoch: Option<EpochNumber>,
678 ) -> Result<Self> {
679 let payload_byte_len = common.payload_byte_len();
680 let shares = membership
681 .coordinator
682 .stake_table_for_epoch(target_epoch)?
683 .stake_table()
684 .map(|entry| entry.stake_table_entry.public_key())
685 .zip(shares)
686 .map(|(node, share)| (node.clone(), share.clone()))
687 .collect();
688
689 Ok(Self {
690 view_number,
691 shares,
692 payload_commitment: commit,
693 epoch: data_epoch,
694 target_epoch,
695 payload_byte_len,
696 common,
697 })
698 }
699
700 pub async fn calculate_vid_disperse(
706 payload: &TYPES::BlockPayload,
707 membership: &EpochMembershipCoordinator<TYPES>,
708 view: ViewNumber,
709 target_epoch: Option<EpochNumber>,
710 data_epoch: Option<EpochNumber>,
711 metadata: &<TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
712 ) -> Result<(Self, Duration)> {
713 let target_mem = membership.stake_table_for_epoch(target_epoch)?;
714 let stake_table: Vec<_> = target_mem.stake_table().cloned().collect();
715 let approximate_weights = approximate_weights(&stake_table);
716
717 let txns = payload.encode();
718 let num_txns = txns.len();
719
720 let avidm_param = init_avidm_gf2_param(approximate_weights.total_weight)?;
721
722 let ns_table = parse_ns_table(num_txns, &metadata.encode());
723 let ns_table_clone = ns_table.clone();
724
725 let now = Instant::now();
726 let (commit, common, shares) = spawn_blocking(move || {
727 AvidmGf2Scheme::ns_disperse(
728 &avidm_param,
729 &approximate_weights.weights,
730 &txns,
731 ns_table_clone,
732 )
733 })
734 .await
735 .wrap()
736 .context(error!("Join error"))?
737 .wrap()
738 .context(|err| error!("Failed to calculate VID disperse. Error: {err}"))?;
739 let ns_disperse_duration = now.elapsed();
740
741 Ok((
742 Self::from_membership(
743 view,
744 commit,
745 &shares,
746 common,
747 &target_mem,
748 target_epoch,
749 data_epoch,
750 )
751 .await?,
752 ns_disperse_duration,
753 ))
754 }
755
756 pub fn to_shares(self) -> Vec<AvidmGf2DisperseShare<TYPES>> {
758 self.shares
759 .into_iter()
760 .map(|(recipient_key, share)| AvidmGf2DisperseShare {
761 share,
762 recipient_key,
763 view_number: self.view_number,
764 payload_commitment: self.payload_commitment,
765 epoch: self.epoch,
766 target_epoch: self.target_epoch,
767 common: self.common.clone(),
768 })
769 .collect()
770 }
771
772 pub fn to_share_proposals(
774 self,
775 signature: &<<TYPES as NodeType>::SignatureKey as SignatureKey>::PureAssembledSignatureType,
776 ) -> Vec<Proposal<TYPES, AvidmGf2DisperseShare<TYPES>>> {
777 self.shares
778 .into_iter()
779 .map(|(recipient_key, share)| Proposal {
780 data: AvidmGf2DisperseShare {
781 share,
782 recipient_key,
783 view_number: self.view_number,
784 payload_commitment: self.payload_commitment,
785 epoch: self.epoch,
786 target_epoch: self.target_epoch,
787 common: self.common.clone(),
788 },
789 signature: signature.clone(),
790 _pd: PhantomData,
791 })
792 .collect()
793 }
794
795 pub fn try_from_shares<'a, I>(mut it: I) -> Option<Self>
797 where
798 I: Iterator<Item = &'a AvidmGf2DisperseShare<TYPES>>,
799 {
800 let first_vid_disperse_share = it.next()?.clone();
801 let payload_byte_len = first_vid_disperse_share.common.payload_byte_len();
802 let mut share_map = BTreeMap::new();
803 share_map.insert(
804 first_vid_disperse_share.recipient_key,
805 first_vid_disperse_share.share,
806 );
807 let mut vid_disperse = Self {
808 view_number: first_vid_disperse_share.view_number,
809 epoch: first_vid_disperse_share.epoch,
810 target_epoch: first_vid_disperse_share.target_epoch,
811 payload_commitment: first_vid_disperse_share.payload_commitment,
812 shares: share_map,
813 payload_byte_len,
814 common: first_vid_disperse_share.common,
815 };
816 it.for_each(|vid_disperse_share| {
817 vid_disperse.shares.insert(
818 vid_disperse_share.recipient_key.clone(),
819 vid_disperse_share.share.clone(),
820 );
821 });
822 Some(vid_disperse)
823 }
824
825 pub fn payload_byte_len(&self) -> u32 {
827 self.payload_byte_len as u32
828 }
829}
830
831#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
832pub struct AvidmGf2DisperseShare<TYPES: NodeType> {
834 pub view_number: ViewNumber,
836 pub epoch: Option<EpochNumber>,
838 pub target_epoch: Option<EpochNumber>,
840 pub payload_commitment: AvidmGf2Commitment,
842 pub share: AvidmGf2Share,
844 pub recipient_key: TYPES::SignatureKey,
846 pub common: AvidmGf2Common,
848}
849
850impl<TYPES: NodeType> HasViewNumber for AvidmGf2DisperseShare<TYPES> {
851 fn view_number(&self) -> ViewNumber {
852 self.view_number
853 }
854}
855
856impl<TYPES: NodeType> AvidmGf2DisperseShare<TYPES> {
857 pub fn to_proposal(
859 self,
860 private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
861 ) -> Option<Proposal<TYPES, Self>> {
862 let Ok(signature) =
863 TYPES::SignatureKey::sign(private_key, self.payload_commitment.as_ref())
864 else {
865 tracing::error!("VID: failed to sign dispersal share payload");
866 return None;
867 };
868 Some(Proposal {
869 signature,
870 _pd: PhantomData,
871 data: self,
872 })
873 }
874 pub fn payload_byte_len(&self) -> u32 {
876 self.common.payload_byte_len() as u32
877 }
878 pub fn is_consistent(&self) -> bool {
880 AvidmGf2Scheme::is_consistent(&self.payload_commitment, &self.common)
881 }
882
883 pub fn verify_with_verified_common(&self) -> bool {
886 AvidmGf2Scheme::verify_share_with_verified_common(&self.common, &self.share)
887 .is_ok_and(|r| r.is_ok())
888 }
889
890 pub fn verify(&self, _total_weight: usize) -> bool {
892 self.is_consistent() && self.verify_with_verified_common()
893 }
894}