use std::{
fmt::{Debug, Display},
hash::Hash,
marker::PhantomData,
sync::Arc,
};
use async_lock::RwLock;
use bincode::Options;
use committable::{Commitment, CommitmentBoundsArkless, Committable, RawCommitmentBuilder};
use jf_vid::VidDisperse as JfVidDisperse;
use rand::Rng;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use utils::anytrace::*;
use vbs::version::Version;
use vec1::Vec1;
use vid_disperse::{ADVZDisperse, ADVZDisperseShare, VidDisperseShare2};
use crate::{
drb::DrbResult,
impl_has_epoch, impl_has_none_epoch,
message::{convert_proposal, Proposal, UpgradeLock},
simple_certificate::{
NextEpochQuorumCertificate2, QuorumCertificate, QuorumCertificate2, TimeoutCertificate,
TimeoutCertificate2, UpgradeCertificate, ViewSyncFinalizeCertificate,
ViewSyncFinalizeCertificate2,
},
simple_vote::{HasEpoch, QuorumData, QuorumData2, UpgradeProposalData, VersionedVoteData},
traits::{
block_contents::{
vid_commitment, BlockHeader, BuilderFee, EncodeBytes, TestableBlock,
GENESIS_VID_NUM_STORAGE_NODES,
},
node_implementation::{ConsensusTime, NodeType, Versions},
signature_key::SignatureKey,
states::TestableState,
BlockPayload,
},
utils::{bincode_opts, genesis_epoch_from_version, option_epoch_from_block_number},
vid::{VidCommitment, VidCommon, VidSchemeType},
vote::{Certificate, HasViewNumber},
};
macro_rules! impl_u64_wrapper {
($t:ty) => {
impl ConsensusTime for $t {
fn genesis() -> Self {
Self(0)
}
fn new(n: u64) -> Self {
Self(n)
}
fn u64(&self) -> u64 {
self.0
}
}
impl Display for $t {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::ops::Add<u64> for $t {
type Output = $t;
fn add(self, rhs: u64) -> Self::Output {
Self(self.0 + rhs)
}
}
impl std::ops::AddAssign<u64> for $t {
fn add_assign(&mut self, rhs: u64) {
self.0 += rhs;
}
}
impl std::ops::Deref for $t {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::Sub<u64> for $t {
type Output = $t;
fn sub(self, rhs: u64) -> Self::Output {
Self(self.0 - rhs)
}
}
};
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ViewNumber(u64);
impl Committable for ViewNumber {
fn commit(&self) -> Commitment<Self> {
let builder = RawCommitmentBuilder::new("View Number Commitment");
builder.u64(self.0).finalize()
}
}
impl_u64_wrapper!(ViewNumber);
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct EpochNumber(u64);
impl Committable for EpochNumber {
fn commit(&self) -> Commitment<Self> {
let builder = RawCommitmentBuilder::new("Epoch Number Commitment");
builder.u64(self.0).finalize()
}
}
impl_u64_wrapper!(EpochNumber);
impl EpochNumber {
#[allow(dead_code)]
fn genesis() -> Self {
Self(1)
}
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound = "TYPES: NodeType")]
pub struct DaProposal<TYPES: NodeType> {
pub encoded_transactions: Arc<[u8]>,
pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
pub view_number: TYPES::View,
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound = "TYPES: NodeType")]
pub struct DaProposal2<TYPES: NodeType> {
pub encoded_transactions: Arc<[u8]>,
pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
pub view_number: TYPES::View,
pub epoch: Option<TYPES::Epoch>,
}
impl<TYPES: NodeType> From<DaProposal<TYPES>> for DaProposal2<TYPES> {
fn from(da_proposal: DaProposal<TYPES>) -> Self {
Self {
encoded_transactions: da_proposal.encoded_transactions,
metadata: da_proposal.metadata,
view_number: da_proposal.view_number,
epoch: None,
}
}
}
impl<TYPES: NodeType> From<DaProposal2<TYPES>> for DaProposal<TYPES> {
fn from(da_proposal2: DaProposal2<TYPES>) -> Self {
Self {
encoded_transactions: da_proposal2.encoded_transactions,
metadata: da_proposal2.metadata,
view_number: da_proposal2.view_number,
}
}
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound = "TYPES: NodeType")]
pub struct UpgradeProposal<TYPES>
where
TYPES: NodeType,
{
pub upgrade_proposal: UpgradeProposalData<TYPES>,
pub view_number: TYPES::View,
}
pub mod vid_disperse;
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound = "TYPES: NodeType")]
pub enum VidDisperse<TYPES: NodeType> {
V0(vid_disperse::ADVZDisperse<TYPES>),
V1(vid_disperse::ADVZDisperse<TYPES>),
}
impl<TYPES: NodeType> From<vid_disperse::ADVZDisperse<TYPES>> for VidDisperse<TYPES> {
fn from(disperse: vid_disperse::ADVZDisperse<TYPES>) -> Self {
Self::V0(disperse)
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperse<TYPES> {
fn view_number(&self) -> TYPES::View {
match self {
Self::V0(disperse) | Self::V1(disperse) => disperse.view_number(),
}
}
}
impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperse<TYPES> {
fn epoch(&self) -> Option<TYPES::Epoch> {
match self {
Self::V0(disperse) | Self::V1(disperse) => disperse.epoch(),
}
}
}
impl<TYPES: NodeType> VidDisperse<TYPES> {
pub async fn from_membership(
view_number: TYPES::View,
vid_disperse: JfVidDisperse<VidSchemeType>,
membership: &Arc<RwLock<TYPES::Membership>>,
target_epoch: Option<TYPES::Epoch>,
data_epoch: Option<TYPES::Epoch>,
data_epoch_payload_commitment: Option<VidCommitment>,
) -> Self {
Self::V0(
ADVZDisperse::from_membership(
view_number,
vid_disperse,
membership,
target_epoch,
data_epoch,
data_epoch_payload_commitment,
)
.await,
)
}
#[allow(clippy::panic)]
pub async fn calculate_vid_disperse<V: Versions>(
payload: &TYPES::BlockPayload,
membership: &Arc<RwLock<TYPES::Membership>>,
view: TYPES::View,
target_epoch: Option<TYPES::Epoch>,
data_epoch: Option<TYPES::Epoch>,
_upgrade_lock: &UpgradeLock<TYPES, V>,
) -> Result<Self> {
ADVZDisperse::calculate_vid_disperse(payload, membership, view, target_epoch, data_epoch)
.await
.map(|result| match data_epoch {
None => Self::V0(result),
Some(_) => Self::V1(result),
})
}
pub fn vid_common_ref(&self) -> &VidCommon {
match self {
Self::V0(disperse) | Self::V1(disperse) => &disperse.common,
}
}
pub fn payload_commitment(&self) -> VidCommitment {
match self {
Self::V0(disperse) | Self::V1(disperse) => disperse.payload_commitment,
}
}
pub fn as_advz(self) -> ADVZDisperse<TYPES> {
match self {
Self::V0(disperse) | Self::V1(disperse) => disperse,
}
}
pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
match self {
Self::V0(share) | Self::V1(share) => share.view_number = view_number,
}
}
}
#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound = "TYPES: NodeType")]
pub enum VidDisperseShare<TYPES: NodeType> {
V0(vid_disperse::ADVZDisperseShare<TYPES>),
V1(vid_disperse::VidDisperseShare2<TYPES>),
}
impl<TYPES: NodeType> VidDisperseShare<TYPES> {
pub fn from_vid_disperse(vid_disperse: VidDisperse<TYPES>) -> Vec<Self> {
match vid_disperse {
VidDisperse::V0(vid_disperse) => {
ADVZDisperseShare::<TYPES>::from_advz_disperse(vid_disperse)
.into_iter()
.map(|share| Self::V0(share))
.collect()
}
VidDisperse::V1(vid_disperse) => {
VidDisperseShare2::<TYPES>::from_vid_disperse(vid_disperse)
.into_iter()
.map(|share| Self::V1(share))
.collect()
}
}
}
pub fn to_proposal(
self,
private_key: &<TYPES::SignatureKey as SignatureKey>::PrivateKey,
) -> Option<Proposal<TYPES, Self>> {
let payload_commitment_ref: &[u8] = match &self {
Self::V0(share) => share.payload_commitment.as_ref(),
Self::V1(share) => share.payload_commitment.as_ref(),
};
let Ok(signature) = TYPES::SignatureKey::sign(private_key, payload_commitment_ref) else {
tracing::error!("VID: failed to sign dispersal share payload");
return None;
};
Some(Proposal {
signature,
_pd: PhantomData,
data: self,
})
}
pub fn to_vid_share_proposals(
vid_disperse_proposal: Proposal<TYPES, VidDisperse<TYPES>>,
) -> Vec<Proposal<TYPES, Self>> {
match vid_disperse_proposal.data {
VidDisperse::V0(disperse) => ADVZDisperseShare::to_vid_share_proposals(
disperse,
&vid_disperse_proposal.signature,
)
.into_iter()
.map(|proposal| convert_proposal(proposal))
.collect(),
VidDisperse::V1(disperse) => VidDisperseShare2::to_vid_share_proposals(
disperse,
&vid_disperse_proposal.signature,
)
.into_iter()
.map(|proposal| convert_proposal(proposal))
.collect(),
}
}
pub fn recipient_key(&self) -> &TYPES::SignatureKey {
match self {
Self::V0(share) => &share.recipient_key,
Self::V1(share) => &share.recipient_key,
}
}
pub fn payload_commitment_ref(&self) -> &[u8] {
match self {
Self::V0(share) => share.payload_commitment.as_ref(),
Self::V1(share) => share.payload_commitment.as_ref(),
}
}
pub fn payload_commitment(&self) -> VidCommitment {
match self {
Self::V0(share) => share.payload_commitment,
Self::V1(share) => share.payload_commitment,
}
}
pub fn data_epoch_payload_commitment(&self) -> Option<VidCommitment> {
match self {
Self::V0(_) => None,
Self::V1(share) => share.data_epoch_payload_commitment,
}
}
pub fn vid_common_ref(&self) -> &VidCommon {
match self {
Self::V0(share) => &share.common,
Self::V1(share) => &share.common,
}
}
pub fn target_epoch(&self) -> Option<<TYPES as NodeType>::Epoch> {
match self {
Self::V0(_) => None,
Self::V1(share) => share.target_epoch,
}
}
#[allow(clippy::result_unit_err)]
pub fn verify_share(&self, total_nodes: usize) -> std::result::Result<(), ()> {
match self {
Self::V0(share) => share.verify_share(total_nodes),
Self::V1(share) => share.verify_share(total_nodes),
}
}
pub fn set_view_number(&mut self, view_number: <TYPES as NodeType>::View) {
match self {
Self::V0(share) => share.view_number = view_number,
Self::V1(share) => share.view_number = view_number,
}
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for VidDisperseShare<TYPES> {
fn view_number(&self) -> TYPES::View {
match self {
Self::V0(disperse) => disperse.view_number(),
Self::V1(disperse) => disperse.view_number(),
}
}
}
impl<TYPES: NodeType> HasEpoch<TYPES> for VidDisperseShare<TYPES> {
fn epoch(&self) -> Option<TYPES::Epoch> {
match self {
Self::V0(_) => None,
Self::V1(share) => share.epoch(),
}
}
}
impl<TYPES: NodeType> From<vid_disperse::ADVZDisperseShare<TYPES>> for VidDisperseShare<TYPES> {
fn from(share: vid_disperse::ADVZDisperseShare<TYPES>) -> Self {
Self::V0(share)
}
}
impl<TYPES: NodeType> From<vid_disperse::VidDisperseShare2<TYPES>> for VidDisperseShare<TYPES> {
fn from(share: vid_disperse::VidDisperseShare2<TYPES>) -> Self {
Self::V1(share)
}
}
impl<TYPES: NodeType> From<VidDisperseShare<TYPES>> for vid_disperse::VidDisperseShare2<TYPES> {
fn from(share: VidDisperseShare<TYPES>) -> vid_disperse::VidDisperseShare2<TYPES> {
match share {
VidDisperseShare::V0(share) => share.into(),
VidDisperseShare::V1(share) => share,
}
}
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound(deserialize = ""))]
pub enum ViewChangeEvidence<TYPES: NodeType> {
Timeout(TimeoutCertificate<TYPES>),
ViewSync(ViewSyncFinalizeCertificate<TYPES>),
}
impl<TYPES: NodeType> ViewChangeEvidence<TYPES> {
pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
match self {
ViewChangeEvidence::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
ViewChangeEvidence::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
}
}
pub fn to_evidence2(self) -> ViewChangeEvidence2<TYPES> {
match self {
ViewChangeEvidence::Timeout(timeout_cert) => {
ViewChangeEvidence2::Timeout(timeout_cert.to_tc2())
}
ViewChangeEvidence::ViewSync(view_sync_cert) => {
ViewChangeEvidence2::ViewSync(view_sync_cert.to_vsc2())
}
}
}
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound(deserialize = ""))]
pub enum ViewChangeEvidence2<TYPES: NodeType> {
Timeout(TimeoutCertificate2<TYPES>),
ViewSync(ViewSyncFinalizeCertificate2<TYPES>),
}
impl<TYPES: NodeType> ViewChangeEvidence2<TYPES> {
pub fn is_valid_for_view(&self, view: &TYPES::View) -> bool {
match self {
ViewChangeEvidence2::Timeout(timeout_cert) => timeout_cert.data().view == *view - 1,
ViewChangeEvidence2::ViewSync(view_sync_cert) => view_sync_cert.view_number == *view,
}
}
pub fn to_evidence(self) -> ViewChangeEvidence<TYPES> {
match self {
ViewChangeEvidence2::Timeout(timeout_cert) => {
ViewChangeEvidence::Timeout(timeout_cert.to_tc())
}
ViewChangeEvidence2::ViewSync(view_sync_cert) => {
ViewChangeEvidence::ViewSync(view_sync_cert.to_vsc())
}
}
}
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound(deserialize = ""))]
pub struct QuorumProposal<TYPES: NodeType> {
pub block_header: TYPES::BlockHeader,
pub view_number: TYPES::View,
pub justify_qc: QuorumCertificate<TYPES>,
pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
pub proposal_certificate: Option<ViewChangeEvidence<TYPES>>,
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound(deserialize = ""))]
pub struct QuorumProposal2<TYPES: NodeType> {
pub block_header: TYPES::BlockHeader,
pub view_number: TYPES::View,
pub epoch: Option<TYPES::Epoch>,
pub justify_qc: QuorumCertificate2<TYPES>,
pub next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
pub upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
#[serde(with = "serde_bytes")]
pub next_drb_result: Option<DrbResult>,
}
#[derive(derive_more::Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
#[serde(bound(deserialize = ""))]
pub struct QuorumProposalWrapper<TYPES: NodeType> {
pub proposal: QuorumProposal2<TYPES>,
}
impl<TYPES: NodeType> QuorumProposal2<TYPES> {
pub async fn validate_epoch<V: Versions>(
&self,
upgrade_lock: &UpgradeLock<TYPES, V>,
epoch_height: u64,
) -> Result<()> {
let calculated_epoch = option_epoch_from_block_number::<TYPES>(
upgrade_lock.epochs_enabled(self.view_number()).await,
self.block_header.block_number(),
epoch_height,
);
ensure!(
calculated_epoch == self.epoch(),
"Quorum proposal invalid: inconsistent epoch."
);
Ok(())
}
}
impl<TYPES: NodeType> QuorumProposalWrapper<TYPES> {
pub fn block_header(&self) -> &TYPES::BlockHeader {
&self.proposal.block_header
}
pub fn view_number(&self) -> TYPES::View {
self.proposal.view_number
}
pub fn justify_qc(&self) -> &QuorumCertificate2<TYPES> {
&self.proposal.justify_qc
}
pub fn next_epoch_justify_qc(&self) -> &Option<NextEpochQuorumCertificate2<TYPES>> {
&self.proposal.next_epoch_justify_qc
}
pub fn upgrade_certificate(&self) -> &Option<UpgradeCertificate<TYPES>> {
&self.proposal.upgrade_certificate
}
pub fn view_change_evidence(&self) -> &Option<ViewChangeEvidence2<TYPES>> {
&self.proposal.view_change_evidence
}
pub fn next_drb_result(&self) -> &Option<DrbResult> {
&self.proposal.next_drb_result
}
pub async fn validate_epoch<V: Versions>(
&self,
upgrade_lock: &UpgradeLock<TYPES, V>,
epoch_height: u64,
) -> Result<()> {
self.proposal
.validate_epoch(upgrade_lock, epoch_height)
.await
}
}
impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposalWrapper<TYPES> {
fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
Self {
proposal: quorum_proposal.into(),
}
}
}
impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposalWrapper<TYPES> {
fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
Self {
proposal: quorum_proposal2,
}
}
}
impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal<TYPES> {
fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
quorum_proposal_wrapper.proposal.into()
}
}
impl<TYPES: NodeType> From<QuorumProposalWrapper<TYPES>> for QuorumProposal2<TYPES> {
fn from(quorum_proposal_wrapper: QuorumProposalWrapper<TYPES>) -> Self {
quorum_proposal_wrapper.proposal
}
}
impl<TYPES: NodeType> From<QuorumProposal<TYPES>> for QuorumProposal2<TYPES> {
fn from(quorum_proposal: QuorumProposal<TYPES>) -> Self {
Self {
block_header: quorum_proposal.block_header,
view_number: quorum_proposal.view_number,
epoch: None,
justify_qc: quorum_proposal.justify_qc.to_qc2(),
next_epoch_justify_qc: None,
upgrade_certificate: quorum_proposal.upgrade_certificate,
view_change_evidence: quorum_proposal
.proposal_certificate
.map(ViewChangeEvidence::to_evidence2),
next_drb_result: None,
}
}
}
impl<TYPES: NodeType> From<QuorumProposal2<TYPES>> for QuorumProposal<TYPES> {
fn from(quorum_proposal2: QuorumProposal2<TYPES>) -> Self {
Self {
block_header: quorum_proposal2.block_header,
view_number: quorum_proposal2.view_number,
justify_qc: quorum_proposal2.justify_qc.to_qc(),
upgrade_certificate: quorum_proposal2.upgrade_certificate,
proposal_certificate: quorum_proposal2
.view_change_evidence
.map(ViewChangeEvidence2::to_evidence),
}
}
}
impl<TYPES: NodeType> From<Leaf<TYPES>> for Leaf2<TYPES> {
fn from(leaf: Leaf<TYPES>) -> Self {
let bytes: [u8; 32] = leaf.parent_commitment.into();
Self {
view_number: leaf.view_number,
justify_qc: leaf.justify_qc.to_qc2(),
next_epoch_justify_qc: None,
parent_commitment: Commitment::from_raw(bytes),
block_header: leaf.block_header,
upgrade_certificate: leaf.upgrade_certificate,
block_payload: leaf.block_payload,
view_change_evidence: None,
next_drb_result: None,
with_epoch: false,
}
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal<TYPES> {
fn view_number(&self) -> TYPES::View {
self.view_number
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for DaProposal2<TYPES> {
fn view_number(&self) -> TYPES::View {
self.view_number
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal<TYPES> {
fn view_number(&self) -> TYPES::View {
self.view_number
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposal2<TYPES> {
fn view_number(&self) -> TYPES::View {
self.view_number
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for QuorumProposalWrapper<TYPES> {
fn view_number(&self) -> TYPES::View {
self.proposal.view_number
}
}
impl<TYPES: NodeType> HasViewNumber<TYPES> for UpgradeProposal<TYPES> {
fn view_number(&self) -> TYPES::View {
self.view_number
}
}
impl_has_epoch!(QuorumProposal2<TYPES>, DaProposal2<TYPES>);
impl_has_none_epoch!(
QuorumProposal<TYPES>,
DaProposal<TYPES>,
UpgradeProposal<TYPES>,
ADVZDisperseShare<TYPES>
);
impl<TYPES: NodeType> HasEpoch<TYPES> for QuorumProposalWrapper<TYPES> {
#[allow(clippy::panic)]
fn epoch(&self) -> Option<TYPES::Epoch> {
self.proposal.epoch()
}
}
#[derive(Error, Debug, Serialize, Deserialize)]
pub enum BlockError {
#[error("Invalid block header: {0}")]
InvalidBlockHeader(String),
#[error("Inconsistent payload commitment")]
InconsistentPayloadCommitment,
}
pub trait TestableLeaf {
type NodeType: NodeType;
fn create_random_transaction(
&self,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction;
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
#[serde(bound(deserialize = ""))]
pub struct Leaf<TYPES: NodeType> {
view_number: TYPES::View,
justify_qc: QuorumCertificate<TYPES>,
parent_commitment: Commitment<Self>,
block_header: TYPES::BlockHeader,
upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
block_payload: Option<TYPES::BlockPayload>,
}
#[derive(Serialize, Deserialize, Clone, Debug, Eq)]
#[serde(bound(deserialize = ""))]
pub struct Leaf2<TYPES: NodeType> {
view_number: TYPES::View,
justify_qc: QuorumCertificate2<TYPES>,
next_epoch_justify_qc: Option<NextEpochQuorumCertificate2<TYPES>>,
parent_commitment: Commitment<Self>,
block_header: TYPES::BlockHeader,
upgrade_certificate: Option<UpgradeCertificate<TYPES>>,
block_payload: Option<TYPES::BlockPayload>,
pub view_change_evidence: Option<ViewChangeEvidence2<TYPES>>,
#[serde(with = "serde_bytes")]
pub next_drb_result: Option<DrbResult>,
pub with_epoch: bool,
}
impl<TYPES: NodeType> Leaf2<TYPES> {
#[must_use]
pub async fn genesis<V: Versions>(
validated_state: &TYPES::ValidatedState,
instance_state: &TYPES::InstanceState,
) -> Self {
let epoch = genesis_epoch_from_version::<V, TYPES>();
let (payload, metadata) =
TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
.await
.unwrap();
let builder_commitment = payload.builder_commitment(&metadata);
let payload_bytes = payload.encode();
let genesis_view = TYPES::View::genesis();
let upgrade_lock = UpgradeLock::<TYPES, V>::new();
let genesis_version = upgrade_lock.version_infallible(genesis_view).await;
let payload_commitment = vid_commitment::<V>(
&payload_bytes,
GENESIS_VID_NUM_STORAGE_NODES,
genesis_version,
);
let block_header = TYPES::BlockHeader::genesis(
instance_state,
payload_commitment,
builder_commitment,
metadata,
);
let null_quorum_data = QuorumData2 {
leaf_commit: Commitment::<Leaf2<TYPES>>::default_commitment_no_preimage(),
epoch,
};
let justify_qc = QuorumCertificate2::new(
null_quorum_data.clone(),
null_quorum_data.commit(),
genesis_view,
None,
PhantomData,
);
Self {
view_number: genesis_view,
justify_qc,
next_epoch_justify_qc: None,
parent_commitment: null_quorum_data.leaf_commit,
upgrade_certificate: None,
block_header: block_header.clone(),
block_payload: Some(payload),
view_change_evidence: None,
next_drb_result: None,
with_epoch: epoch.is_some(),
}
}
pub fn view_number(&self) -> TYPES::View {
self.view_number
}
pub fn epoch(&self, epoch_height: u64) -> Option<TYPES::Epoch> {
option_epoch_from_block_number::<TYPES>(
self.with_epoch,
self.block_header.block_number(),
epoch_height,
)
}
pub fn height(&self) -> u64 {
self.block_header.block_number()
}
pub fn justify_qc(&self) -> QuorumCertificate2<TYPES> {
self.justify_qc.clone()
}
pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
self.upgrade_certificate.clone()
}
pub fn parent_commitment(&self) -> Commitment<Self> {
self.parent_commitment
}
pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
&self.block_header
}
pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
&mut self.block_header
}
pub fn fill_block_payload<V: Versions>(
&mut self,
block_payload: TYPES::BlockPayload,
num_storage_nodes: usize,
version: Version,
) -> std::result::Result<(), BlockError> {
let encoded_txns = block_payload.encode();
let commitment = vid_commitment::<V>(&encoded_txns, num_storage_nodes, version);
if commitment != self.block_header.payload_commitment() {
return Err(BlockError::InconsistentPayloadCommitment);
}
self.block_payload = Some(block_payload);
Ok(())
}
pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
self.block_payload.take()
}
pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
self.block_payload = Some(block_payload);
}
pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
self.block_payload.clone()
}
pub fn payload_commitment(&self) -> VidCommitment {
self.block_header().payload_commitment()
}
pub async fn extends_upgrade(
&self,
parent: &Self,
decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
) -> Result<()> {
match (self.upgrade_certificate(), parent.upgrade_certificate()) {
(None | Some(_), None) => {}
(None, Some(parent_cert)) => {
let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
ensure!(self.view_number() > parent_cert.data.new_version_first_view
|| (self.view_number() > parent_cert.data.decide_by && decided_upgrade_certificate_read.is_none()),
"The new leaf is missing an upgrade certificate that was present in its parent, and should still be live."
);
}
(Some(cert), Some(parent_cert)) => {
ensure!(cert == parent_cert, "The new leaf does not extend the parent leaf, because it has attached a different upgrade certificate.");
}
}
Ok(())
}
pub fn to_leaf_unsafe(self) -> Leaf<TYPES> {
let bytes: [u8; 32] = self.parent_commitment.into();
Leaf {
view_number: self.view_number,
justify_qc: self.justify_qc.to_qc(),
parent_commitment: Commitment::from_raw(bytes),
block_header: self.block_header,
upgrade_certificate: self.upgrade_certificate,
block_payload: self.block_payload,
}
}
}
impl<TYPES: NodeType> Committable for Leaf2<TYPES> {
fn commit(&self) -> committable::Commitment<Self> {
let Leaf2 {
view_number,
justify_qc,
next_epoch_justify_qc,
parent_commitment,
block_header,
upgrade_certificate,
block_payload: _,
view_change_evidence: _,
next_drb_result,
with_epoch,
} = self;
let mut cb = RawCommitmentBuilder::new("leaf commitment")
.u64_field("view number", **view_number)
.field("parent leaf commitment", *parent_commitment)
.field("block header", block_header.commit())
.field("justify qc", justify_qc.commit())
.optional("upgrade certificate", upgrade_certificate);
if *with_epoch {
cb = cb
.constant_str("with_epoch")
.optional("next_epoch_justify_qc", next_epoch_justify_qc);
if let Some(next_drb_result) = next_drb_result {
cb = cb
.constant_str("next_drb_result")
.fixed_size_bytes(next_drb_result);
}
}
cb.finalize()
}
}
impl<TYPES: NodeType> Leaf<TYPES> {
#[allow(clippy::unused_async)]
pub async fn commit<V: Versions>(
&self,
_upgrade_lock: &UpgradeLock<TYPES, V>,
) -> Commitment<Self> {
<Self as Committable>::commit(self)
}
}
impl<TYPES: NodeType> PartialEq for Leaf<TYPES> {
fn eq(&self, other: &Self) -> bool {
self.view_number == other.view_number
&& self.justify_qc == other.justify_qc
&& self.parent_commitment == other.parent_commitment
&& self.block_header == other.block_header
}
}
impl<TYPES: NodeType> PartialEq for Leaf2<TYPES> {
fn eq(&self, other: &Self) -> bool {
let Leaf2 {
view_number,
justify_qc,
next_epoch_justify_qc,
parent_commitment,
block_header,
upgrade_certificate,
block_payload: _,
view_change_evidence,
next_drb_result,
with_epoch,
} = self;
*view_number == other.view_number
&& *justify_qc == other.justify_qc
&& *next_epoch_justify_qc == other.next_epoch_justify_qc
&& *parent_commitment == other.parent_commitment
&& *block_header == other.block_header
&& *upgrade_certificate == other.upgrade_certificate
&& *view_change_evidence == other.view_change_evidence
&& *next_drb_result == other.next_drb_result
&& *with_epoch == other.with_epoch
}
}
impl<TYPES: NodeType> Hash for Leaf<TYPES> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.view_number.hash(state);
self.justify_qc.hash(state);
self.parent_commitment.hash(state);
self.block_header.hash(state);
}
}
impl<TYPES: NodeType> Hash for Leaf2<TYPES> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.commit().hash(state);
self.view_number.hash(state);
self.justify_qc.hash(state);
self.parent_commitment.hash(state);
self.block_header.hash(state);
}
}
impl<TYPES: NodeType> Display for Leaf<TYPES> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"view: {:?}, height: {:?}, justify: {}",
self.view_number,
self.height(),
self.justify_qc
)
}
}
impl<TYPES: NodeType> QuorumCertificate<TYPES> {
#[must_use]
pub async fn genesis<V: Versions>(
validated_state: &TYPES::ValidatedState,
instance_state: &TYPES::InstanceState,
) -> Self {
let upgrade_lock = UpgradeLock::<TYPES, V>::new();
let genesis_view = <TYPES::View as ConsensusTime>::genesis();
let data = QuorumData {
leaf_commit: Leaf::genesis::<V>(validated_state, instance_state)
.await
.commit(&upgrade_lock)
.await,
};
let versioned_data =
VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
.await;
let bytes: [u8; 32] = versioned_data.commit().into();
Self::new(
data,
Commitment::from_raw(bytes),
genesis_view,
None,
PhantomData,
)
}
}
impl<TYPES: NodeType> QuorumCertificate2<TYPES> {
#[must_use]
pub async fn genesis<V: Versions>(
validated_state: &TYPES::ValidatedState,
instance_state: &TYPES::InstanceState,
) -> Self {
let upgrade_lock = UpgradeLock::<TYPES, V>::new();
let genesis_view = <TYPES::View as ConsensusTime>::genesis();
let data = QuorumData2 {
leaf_commit: Leaf2::genesis::<V>(validated_state, instance_state)
.await
.commit(),
epoch: genesis_epoch_from_version::<V, TYPES>(), };
let versioned_data =
VersionedVoteData::<_, _, V>::new_infallible(data.clone(), genesis_view, &upgrade_lock)
.await;
let bytes: [u8; 32] = versioned_data.commit().into();
Self::new(
data,
Commitment::from_raw(bytes),
genesis_view,
None,
PhantomData,
)
}
}
impl<TYPES: NodeType> Leaf<TYPES> {
#[must_use]
pub async fn genesis<V: Versions>(
validated_state: &TYPES::ValidatedState,
instance_state: &TYPES::InstanceState,
) -> Self {
let (payload, metadata) =
TYPES::BlockPayload::from_transactions([], validated_state, instance_state)
.await
.unwrap();
let builder_commitment = payload.builder_commitment(&metadata);
let payload_bytes = payload.encode();
let genesis_view = TYPES::View::genesis();
let upgrade_lock = UpgradeLock::<TYPES, V>::new();
let genesis_version = upgrade_lock.version_infallible(genesis_view).await;
let payload_commitment = vid_commitment::<V>(
&payload_bytes,
GENESIS_VID_NUM_STORAGE_NODES,
genesis_version,
);
let block_header = TYPES::BlockHeader::genesis(
instance_state,
payload_commitment,
builder_commitment,
metadata,
);
let null_quorum_data = QuorumData {
leaf_commit: Commitment::<Leaf<TYPES>>::default_commitment_no_preimage(),
};
let justify_qc = QuorumCertificate::new(
null_quorum_data.clone(),
null_quorum_data.commit(),
genesis_view,
None,
PhantomData,
);
Self {
view_number: genesis_view,
justify_qc,
parent_commitment: null_quorum_data.leaf_commit,
upgrade_certificate: None,
block_header: block_header.clone(),
block_payload: Some(payload),
}
}
pub fn view_number(&self) -> TYPES::View {
self.view_number
}
pub fn height(&self) -> u64 {
self.block_header.block_number()
}
pub fn justify_qc(&self) -> QuorumCertificate<TYPES> {
self.justify_qc.clone()
}
pub fn upgrade_certificate(&self) -> Option<UpgradeCertificate<TYPES>> {
self.upgrade_certificate.clone()
}
pub fn parent_commitment(&self) -> Commitment<Self> {
self.parent_commitment
}
pub fn block_header(&self) -> &<TYPES as NodeType>::BlockHeader {
&self.block_header
}
pub fn block_header_mut(&mut self) -> &mut <TYPES as NodeType>::BlockHeader {
&mut self.block_header
}
pub fn fill_block_payload<V: Versions>(
&mut self,
block_payload: TYPES::BlockPayload,
num_storage_nodes: usize,
version: Version,
) -> std::result::Result<(), BlockError> {
let encoded_txns = block_payload.encode();
let commitment = vid_commitment::<V>(&encoded_txns, num_storage_nodes, version);
if commitment != self.block_header.payload_commitment() {
return Err(BlockError::InconsistentPayloadCommitment);
}
self.block_payload = Some(block_payload);
Ok(())
}
pub fn unfill_block_payload(&mut self) -> Option<TYPES::BlockPayload> {
self.block_payload.take()
}
pub fn fill_block_payload_unchecked(&mut self, block_payload: TYPES::BlockPayload) {
self.block_payload = Some(block_payload);
}
pub fn block_payload(&self) -> Option<TYPES::BlockPayload> {
self.block_payload.clone()
}
pub fn payload_commitment(&self) -> VidCommitment {
self.block_header().payload_commitment()
}
pub async fn extends_upgrade(
&self,
parent: &Self,
decided_upgrade_certificate: &Arc<RwLock<Option<UpgradeCertificate<TYPES>>>>,
) -> Result<()> {
match (self.upgrade_certificate(), parent.upgrade_certificate()) {
(None | Some(_), None) => {}
(None, Some(parent_cert)) => {
let decided_upgrade_certificate_read = decided_upgrade_certificate.read().await;
ensure!(self.view_number() > parent_cert.data.new_version_first_view
|| (self.view_number() > parent_cert.data.decide_by && decided_upgrade_certificate_read.is_none()),
"The new leaf is missing an upgrade certificate that was present in its parent, and should still be live."
);
}
(Some(cert), Some(parent_cert)) => {
ensure!(cert == parent_cert, "The new leaf does not extend the parent leaf, because it has attached a different upgrade certificate.");
}
}
Ok(())
}
}
impl<TYPES: NodeType> TestableLeaf for Leaf<TYPES>
where
TYPES::ValidatedState: TestableState<TYPES>,
TYPES::BlockPayload: TestableBlock<TYPES>,
{
type NodeType = TYPES;
fn create_random_transaction(
&self,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
{
TYPES::ValidatedState::create_random_transaction(None, rng, padding)
}
}
impl<TYPES: NodeType> TestableLeaf for Leaf2<TYPES>
where
TYPES::ValidatedState: TestableState<TYPES>,
TYPES::BlockPayload: TestableBlock<TYPES>,
{
type NodeType = TYPES;
fn create_random_transaction(
&self,
rng: &mut dyn rand::RngCore,
padding: u64,
) -> <<Self::NodeType as NodeType>::BlockPayload as BlockPayload<Self::NodeType>>::Transaction
{
TYPES::ValidatedState::create_random_transaction(None, rng, padding)
}
}
#[must_use]
pub fn fake_commitment<S: Committable>() -> Commitment<S> {
RawCommitmentBuilder::new("Dummy commitment for arbitrary genesis").finalize()
}
#[must_use]
pub fn random_commitment<S: Committable>(rng: &mut dyn rand::RngCore) -> Commitment<S> {
let random_array: Vec<u8> = (0u8..100u8).map(|_| rng.gen_range(0..255)).collect();
RawCommitmentBuilder::new("Random Commitment")
.constant_str("Random Field")
.var_size_bytes(&random_array)
.finalize()
}
pub fn serialize_signature2<TYPES: NodeType>(
signatures: &<TYPES::SignatureKey as SignatureKey>::QcType,
) -> Vec<u8> {
let mut signatures_bytes = vec![];
signatures_bytes.extend("Yes".as_bytes());
let (sig, proof) = TYPES::SignatureKey::sig_proof(signatures);
let proof_bytes = bincode_opts()
.serialize(&proof.as_bitslice())
.expect("This serialization shouldn't be able to fail");
signatures_bytes.extend("bitvec proof".as_bytes());
signatures_bytes.extend(proof_bytes.as_slice());
let sig_bytes = bincode_opts()
.serialize(&sig)
.expect("This serialization shouldn't be able to fail");
signatures_bytes.extend("aggregated signature".as_bytes());
signatures_bytes.extend(sig_bytes.as_slice());
signatures_bytes
}
impl<TYPES: NodeType> Committable for Leaf<TYPES> {
fn commit(&self) -> committable::Commitment<Self> {
RawCommitmentBuilder::new("leaf commitment")
.u64_field("view number", *self.view_number)
.field("parent leaf commitment", self.parent_commitment)
.field("block header", self.block_header.commit())
.field("justify qc", self.justify_qc.commit())
.optional("upgrade certificate", &self.upgrade_certificate)
.finalize()
}
}
impl<TYPES: NodeType> Leaf2<TYPES> {
pub fn from_quorum_proposal(quorum_proposal: &QuorumProposalWrapper<TYPES>) -> Self {
let QuorumProposalWrapper {
proposal:
QuorumProposal2 {
view_number,
epoch,
justify_qc,
next_epoch_justify_qc,
block_header,
upgrade_certificate,
view_change_evidence,
next_drb_result,
},
} = quorum_proposal;
Self {
view_number: *view_number,
justify_qc: justify_qc.clone(),
next_epoch_justify_qc: next_epoch_justify_qc.clone(),
parent_commitment: justify_qc.data().leaf_commit,
block_header: block_header.clone(),
upgrade_certificate: upgrade_certificate.clone(),
block_payload: None,
view_change_evidence: view_change_evidence.clone(),
next_drb_result: *next_drb_result,
with_epoch: epoch.is_some(),
}
}
}
impl<TYPES: NodeType> Leaf<TYPES> {
pub fn from_quorum_proposal(quorum_proposal: &QuorumProposal<TYPES>) -> Self {
let QuorumProposal {
view_number,
justify_qc,
block_header,
upgrade_certificate,
proposal_certificate: _,
} = quorum_proposal;
Self {
view_number: *view_number,
justify_qc: justify_qc.clone(),
parent_commitment: justify_qc.data().leaf_commit,
block_header: block_header.clone(),
upgrade_certificate: upgrade_certificate.clone(),
block_payload: None,
}
}
}
pub mod null_block {
#![allow(missing_docs)]
use jf_vid::VidScheme;
use vbs::version::StaticVersionType;
use crate::{
traits::{
block_contents::BuilderFee,
node_implementation::{NodeType, Versions},
signature_key::BuilderSignatureKey,
BlockPayload,
},
vid::{advz_scheme, VidCommitment},
};
#[must_use]
pub fn commitment<V: Versions>(num_storage_nodes: usize) -> Option<VidCommitment> {
let vid_result = advz_scheme(num_storage_nodes).commit_only(Vec::new());
match vid_result {
Ok(r) => Some(r),
Err(_) => None,
}
}
#[must_use]
pub fn builder_fee<TYPES: NodeType, V: Versions>(
num_storage_nodes: usize,
version: vbs::version::Version,
view_number: u64,
) -> Option<BuilderFee<TYPES>> {
const FEE_AMOUNT: u64 = 0;
let (pub_key, priv_key) =
<TYPES::BuilderSignatureKey as BuilderSignatureKey>::generated_from_seed_indexed(
[0_u8; 32], 0,
);
if version >= V::Marketplace::VERSION {
match TYPES::BuilderSignatureKey::sign_sequencing_fee_marketplace(
&priv_key,
FEE_AMOUNT,
view_number,
) {
Ok(sig) => Some(BuilderFee {
fee_amount: FEE_AMOUNT,
fee_account: pub_key,
fee_signature: sig,
}),
Err(_) => None,
}
} else {
let (_null_block, null_block_metadata) =
<TYPES::BlockPayload as BlockPayload<TYPES>>::empty();
match TYPES::BuilderSignatureKey::sign_fee(
&priv_key,
FEE_AMOUNT,
&null_block_metadata,
&commitment::<V>(num_storage_nodes)?,
) {
Ok(sig) => Some(BuilderFee {
fee_amount: FEE_AMOUNT,
fee_account: pub_key,
fee_signature: sig,
}),
Err(_) => None,
}
}
}
}
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct PackedBundle<TYPES: NodeType> {
pub encoded_transactions: Arc<[u8]>,
pub metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
pub view_number: TYPES::View,
pub epoch_number: Option<TYPES::Epoch>,
pub sequencing_fees: Vec1<BuilderFee<TYPES>>,
pub auction_result: Option<TYPES::AuctionResult>,
}
impl<TYPES: NodeType> PackedBundle<TYPES> {
pub fn new(
encoded_transactions: Arc<[u8]>,
metadata: <TYPES::BlockPayload as BlockPayload<TYPES>>::Metadata,
view_number: TYPES::View,
epoch_number: Option<TYPES::Epoch>,
sequencing_fees: Vec1<BuilderFee<TYPES>>,
auction_result: Option<TYPES::AuctionResult>,
) -> Self {
Self {
encoded_transactions,
metadata,
view_number,
epoch_number,
sequencing_fees,
auction_result,
}
}
}