Skip to content

Commit

Permalink
Merge pull request #2350 from AleoHQ/feat/cleanup-cert
Browse files Browse the repository at this point in the history
Remove the outdated version of `BatchCertificate`
  • Loading branch information
howardwu authored Feb 12, 2024
2 parents 276597b + 2efd083 commit e0add02
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 228 deletions.
111 changes: 30 additions & 81 deletions ledger/narwhal/batch-certificate/src/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,97 +20,46 @@ impl<N: Network> FromBytes for BatchCertificate<N> {
// Read the version.
let version = u8::read_le(&mut reader)?;
// Ensure the version is valid.
if version != 1 && version != 2 {
if version != 1 {
return Err(error("Invalid batch certificate version"));
}

if version == 1 {
// Read the certificate ID.
let certificate_id = Field::read_le(&mut reader)?;
// Read the batch header.
let batch_header = BatchHeader::read_le(&mut reader)?;
// Read the number of signatures.
let num_signatures = u32::read_le(&mut reader)?;
// Ensure the number of signatures is within bounds.
if num_signatures > Self::MAX_SIGNATURES as u32 {
return Err(error(format!(
"Number of signatures ({num_signatures}) exceeds the maximum ({})",
Self::MAX_SIGNATURES
)));
}
// Read the signatures.
let mut signatures = IndexMap::with_capacity(num_signatures as usize);
for _ in 0..num_signatures {
// Read the signature.
let signature = Signature::read_le(&mut reader)?;
// Read the timestamp.
let timestamp = i64::read_le(&mut reader)?;
// Insert the signature and timestamp.
signatures.insert(signature, timestamp);
}
// Return the batch certificate.
Self::from_v1_deprecated(certificate_id, batch_header, signatures).map_err(error)
} else if version == 2 {
// Read the batch header.
let batch_header = BatchHeader::read_le(&mut reader)?;
// Read the number of signatures.
let num_signatures = u16::read_le(&mut reader)?;
// Ensure the number of signatures is within bounds.
if num_signatures > Self::MAX_SIGNATURES {
return Err(error(format!(
"Number of signatures ({num_signatures}) exceeds the maximum ({})",
Self::MAX_SIGNATURES
)));
}
// Read the signature bytes.
let mut signature_bytes = vec![0u8; num_signatures as usize * Signature::<N>::size_in_bytes()];
reader.read_exact(&mut signature_bytes)?;
// Read the signatures.
let signatures = cfg_chunks!(signature_bytes, Signature::<N>::size_in_bytes())
.map(Signature::read_le)
.collect::<Result<IndexSet<_>, _>>()?;
// Return the batch certificate.
Self::from(batch_header, signatures).map_err(error)
} else {
unreachable!("Invalid batch certificate version")
// Read the batch header.
let batch_header = BatchHeader::read_le(&mut reader)?;
// Read the number of signatures.
let num_signatures = u16::read_le(&mut reader)?;
// Ensure the number of signatures is within bounds.
if num_signatures > Self::MAX_SIGNATURES {
return Err(error(format!(
"Number of signatures ({num_signatures}) exceeds the maximum ({})",
Self::MAX_SIGNATURES
)));
}
// Read the signature bytes.
let mut signature_bytes = vec![0u8; num_signatures as usize * Signature::<N>::size_in_bytes()];
reader.read_exact(&mut signature_bytes)?;
// Read the signatures.
let signatures = cfg_chunks!(signature_bytes, Signature::<N>::size_in_bytes())
.map(Signature::read_le)
.collect::<Result<IndexSet<_>, _>>()?;
// Return the batch certificate.
Self::from(batch_header, signatures).map_err(error)
}
}

impl<N: Network> ToBytes for BatchCertificate<N> {
/// Writes the batch certificate to the buffer.
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::V1 { certificate_id, batch_header, signatures } => {
// Write the version.
1u8.write_le(&mut writer)?;
// Write the certificate ID.
certificate_id.write_le(&mut writer)?;
// Write the batch header.
batch_header.write_le(&mut writer)?;
// Write the number of signatures.
u32::try_from(signatures.len()).map_err(error)?.write_le(&mut writer)?;
// Write the signatures.
for (signature, timestamp) in signatures.iter() {
// Write the signature.
signature.write_le(&mut writer)?;
// Write the timestamp.
timestamp.write_le(&mut writer)?;
}
}
Self::V2 { batch_header, signatures } => {
// Write the version.
2u8.write_le(&mut writer)?;
// Write the batch header.
batch_header.write_le(&mut writer)?;
// Write the number of signatures.
u16::try_from(signatures.len()).map_err(error)?.write_le(&mut writer)?;
// Write the signatures.
for signature in signatures.iter() {
// Write the signature.
signature.write_le(&mut writer)?;
}
}
// Write the version.
1u8.write_le(&mut writer)?;
// Write the batch header.
self.batch_header.write_le(&mut writer)?;
// Write the number of signatures.
u16::try_from(self.signatures.len()).map_err(error)?.write_le(&mut writer)?;
// Write the signatures.
for signature in self.signatures.iter() {
// Write the signature.
signature.write_le(&mut writer)?;
}
Ok(())
}
Expand Down
105 changes: 12 additions & 93 deletions ledger/narwhal/batch-certificate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,29 +28,18 @@ use narwhal_batch_header::BatchHeader;
use narwhal_transmission_id::TransmissionID;

use core::hash::{Hash, Hasher};
use indexmap::{IndexMap, IndexSet};
use indexmap::IndexSet;
use std::collections::HashSet;

#[cfg(not(feature = "serial"))]
use rayon::prelude::*;

#[derive(Clone)]
pub enum BatchCertificate<N: Network> {
// TODO (howardwu): For mainnet - Delete V1 and switch everyone to V2 as the default.
V1 {
/// The certificate ID.
certificate_id: Field<N>,
/// The batch header.
batch_header: BatchHeader<N>,
/// The `(signature, timestamp)` pairs for the batch ID from the committee.
signatures: IndexMap<Signature<N>, i64>,
},
V2 {
/// The batch header.
batch_header: BatchHeader<N>,
/// The signatures for the batch ID from the committee.
signatures: IndexSet<Signature<N>>,
},
pub struct BatchCertificate<N: Network> {
/// The batch header.
batch_header: BatchHeader<N>,
/// The signatures for the batch ID from the committee.
signatures: IndexSet<Signature<N>>,
}

impl<N: Network> BatchCertificate<N> {
Expand All @@ -59,48 +48,6 @@ impl<N: Network> BatchCertificate<N> {
}

impl<N: Network> BatchCertificate<N> {
// TODO (howardwu): For mainnet - Delete V1 and switch everyone to V2 as the default.
/// Initializes a (deprecated) V1 batch certificate.
pub fn from_v1_deprecated(
certificate_id: Field<N>,
batch_header: BatchHeader<N>,
signatures: IndexMap<Signature<N>, i64>,
) -> Result<Self> {
/// Returns the certificate ID.
fn compute_certificate_id<N: Network>(
batch_id: Field<N>,
signatures: &IndexMap<Signature<N>, i64>,
) -> Result<Field<N>> {
let mut preimage = Vec::new();
// Insert the batch ID.
batch_id.write_le(&mut preimage)?;
// Insert the signatures.
for (signature, timestamp) in signatures {
// Insert the signature.
signature.write_le(&mut preimage)?;
// Insert the timestamp.
timestamp.write_le(&mut preimage)?;
}
// Hash the preimage.
N::hash_bhp1024(&preimage.to_bits_le())
}
// Ensure that the number of signatures is within bounds.
ensure!(signatures.len() <= Self::MAX_SIGNATURES as usize, "Invalid number of signatures");
// Compute the certificate ID.
if certificate_id != compute_certificate_id(batch_header.batch_id(), &signatures)? {
bail!("Invalid batch certificate ID")
}
// Verify the signatures are valid.
for (signature, timestamp) in &signatures {
let preimage = [batch_header.batch_id(), Field::from_u64(*timestamp as u64)];
if !signature.verify(&signature.to_address(), &preimage) {
bail!("Invalid batch certificate signature")
}
}
// Return the V1 batch certificate.
Ok(Self::V1 { certificate_id, batch_header, signatures })
}

/// Initializes a new batch certificate.
pub fn from(batch_header: BatchHeader<N>, signatures: IndexSet<Signature<N>>) -> Result<Self> {
// Ensure that the number of signatures is within bounds.
Expand Down Expand Up @@ -130,7 +77,7 @@ impl<N: Network> BatchCertificate<N> {
// Ensure the signatures are not empty.
ensure!(!signatures.is_empty(), "Batch certificate must contain signatures");
// Return the batch certificate.
Ok(Self::V2 { batch_header, signatures })
Ok(Self { batch_header, signatures })
}
}

Expand All @@ -144,36 +91,19 @@ impl<N: Network> Eq for BatchCertificate<N> {}

impl<N: Network> Hash for BatchCertificate<N> {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Self::V1 { batch_header, signatures, .. } => {
batch_header.batch_id().hash(state);
(signatures.len() as u64).hash(state);
for signature in signatures.iter() {
signature.hash(state);
}
}
Self::V2 { batch_header, .. } => {
batch_header.batch_id().hash(state);
}
}
self.batch_header.batch_id().hash(state);
}
}

impl<N: Network> BatchCertificate<N> {
/// Returns the certificate ID.
pub const fn id(&self) -> Field<N> {
match self {
Self::V1 { certificate_id, .. } => *certificate_id,
Self::V2 { batch_header, .. } => batch_header.batch_id(),
}
self.batch_header.batch_id()
}

/// Returns the batch header.
pub const fn batch_header(&self) -> &BatchHeader<N> {
match self {
Self::V1 { batch_header, .. } => batch_header,
Self::V2 { batch_header, .. } => batch_header,
}
&self.batch_header
}

/// Returns the batch ID.
Expand Down Expand Up @@ -203,23 +133,12 @@ impl<N: Network> BatchCertificate<N> {

/// Returns the timestamp of the batch header.
pub fn timestamp(&self) -> i64 {
match self {
Self::V1 { batch_header, signatures, .. } => {
// Return the median timestamp.
let mut timestamps = signatures.values().copied().chain([batch_header.timestamp()]).collect::<Vec<_>>();
timestamps.sort_unstable();
timestamps[timestamps.len() / 2]
}
Self::V2 { batch_header, .. } => batch_header.timestamp(),
}
self.batch_header().timestamp()
}

/// Returns the signatures of the batch ID from the committee.
pub fn signatures(&self) -> Box<dyn '_ + ExactSizeIterator<Item = &Signature<N>>> {
match self {
Self::V1 { signatures, .. } => Box::new(signatures.keys()),
Self::V2 { signatures, .. } => Box::new(signatures.iter()),
}
Box::new(self.signatures.iter())
}
}

Expand Down
48 changes: 11 additions & 37 deletions ledger/narwhal/batch-certificate/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,12 @@ impl<N: Network> Serialize for BatchCertificate<N> {
/// Serializes the batch certificate to a JSON-string or buffer.
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match serializer.is_human_readable() {
true => match self {
Self::V1 { certificate_id, batch_header, signatures } => {
let mut state = serializer.serialize_struct("BatchCertificate", 3)?;
state.serialize_field("certificate_id", certificate_id)?;
state.serialize_field("batch_header", batch_header)?;
state.serialize_field("signatures", signatures)?;
state.end()
}
Self::V2 { batch_header, signatures } => {
let mut state = serializer.serialize_struct("BatchCertificate", 2)?;
state.serialize_field("batch_header", batch_header)?;
state.serialize_field("signatures", signatures)?;
state.end()
}
},
true => {
let mut state = serializer.serialize_struct("BatchCertificate", 2)?;
state.serialize_field("batch_header", &self.batch_header)?;
state.serialize_field("signatures", &self.signatures)?;
state.end()
}
false => ToBytesSerializer::serialize_with_size_encoding(self, serializer),
}
}
Expand All @@ -44,28 +35,11 @@ impl<'de, N: Network> Deserialize<'de> for BatchCertificate<N> {
match deserializer.is_human_readable() {
true => {
let mut value = serde_json::Value::deserialize(deserializer)?;

// Check if a certificate ID field is present.
let certificate_id = match value.get("certificate_id") {
Some(..) => Some(DeserializeExt::take_from_value::<D>(&mut value, "certificate_id")?),
None => None,
};

// Parse for V1 and V2 batch certificates.
if let Some(certificate_id) = certificate_id {
Self::from_v1_deprecated(
certificate_id,
DeserializeExt::take_from_value::<D>(&mut value, "batch_header")?,
DeserializeExt::take_from_value::<D>(&mut value, "signatures")?,
)
.map_err(de::Error::custom)
} else {
Self::from(
DeserializeExt::take_from_value::<D>(&mut value, "batch_header")?,
DeserializeExt::take_from_value::<D>(&mut value, "signatures")?,
)
.map_err(de::Error::custom)
}
Self::from(
DeserializeExt::take_from_value::<D>(&mut value, "batch_header")?,
DeserializeExt::take_from_value::<D>(&mut value, "signatures")?,
)
.map_err(de::Error::custom)
}
false => FromBytesDeserializer::<Self>::deserialize_with_size_encoding(deserializer, "batch certificate"),
}
Expand Down
29 changes: 12 additions & 17 deletions ledger/narwhal/subdag/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,23 +188,18 @@ impl<N: Network> Subdag<N> {

/// Returns the timestamp of the anchor round, defined as the weighted median timestamp of the subdag.
pub fn timestamp(&self, committee: &Committee<N>) -> i64 {
match self.leader_certificate() {
BatchCertificate::V1 { .. } => self.leader_certificate().timestamp(),
BatchCertificate::V2 { .. } => {
// Retrieve the anchor round.
let anchor_round = self.anchor_round();
// Retrieve the timestamps and stakes of the certificates for `anchor_round` - 1.
let timestamps_and_stakes = self
.values()
.flatten()
.filter(|certificate| certificate.round() == anchor_round.saturating_sub(1))
.map(|certificate| (certificate.timestamp(), committee.get_stake(certificate.author())))
.collect::<Vec<_>>();

// Return the weighted median timestamp.
weighted_median(timestamps_and_stakes)
}
}
// Retrieve the anchor round.
let anchor_round = self.anchor_round();
// Retrieve the timestamps and stakes of the certificates for `anchor_round` - 1.
let timestamps_and_stakes = self
.values()
.flatten()
.filter(|certificate| certificate.round() == anchor_round.saturating_sub(1))
.map(|certificate| (certificate.timestamp(), committee.get_stake(certificate.author())))
.collect::<Vec<_>>();

// Return the weighted median timestamp.
weighted_median(timestamps_and_stakes)
}

/// Returns the subdag root of the certificates.
Expand Down

0 comments on commit e0add02

Please sign in to comment.