use std::{
collections::BTreeSet,
convert::TryFrom,
ops::{Deref, Index},
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{
super::epoch::EpochIdentifier, certificate::HandoverCertificate, errors::HandoverChainError,
record::HandoverRecord,
};
fn validate_canonical_genesis_first(record: &HandoverRecord) -> Result<(), HandoverChainError> {
if record.is_genesis() {
Ok(())
} else {
Err(HandoverChainError::InvalidGenesisFirstRecord)
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct HandoverChain(Vec<HandoverCertificate>);
impl Serialize for HandoverChain {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.0.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for HandoverChain {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let inner = Vec::<HandoverCertificate>::deserialize(deserializer)?;
let chain = Self(inner);
chain.validate().map_err(serde::de::Error::custom)?;
Ok(chain)
}
}
impl TryFrom<Vec<HandoverCertificate>> for HandoverChain {
type Error = HandoverChainError;
fn try_from(value: Vec<HandoverCertificate>) -> Result<Self, Self::Error> {
let chain = Self(value);
chain.validate()?;
Ok(chain)
}
}
impl HandoverChain {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn validate(&self) -> Result<(), HandoverChainError> {
if self.0.is_empty() {
return Ok(());
}
let r0 = self.0[0].handover_record();
if !r0.is_genesis() {
return Err(HandoverChainError::InvalidGenesisFirstRecord);
}
let mut seen = BTreeSet::new();
if !seen.insert(r0.dest_epoch()) {
return Err(HandoverChainError::DuplicateDestEpoch {
epoch: r0.dest_epoch(),
});
}
let mut prev_bi = r0.block_index();
let mut prev_dest = r0.dest_epoch();
let mut prev_hash = self.0[0].handover_record().record_digest();
for cert in self.0.iter().skip(1) {
let cur = cert.handover_record();
if *cur.previous_handover_hash() != prev_hash {
return Err(HandoverChainError::PreviousHashMismatch {
expected: prev_hash,
got: *cur.previous_handover_hash(),
});
}
if !seen.insert(cur.dest_epoch()) {
return Err(HandoverChainError::DuplicateDestEpoch {
epoch: cur.dest_epoch(),
});
}
if cur.block_index() <= prev_bi {
return Err(HandoverChainError::NonAscendingBlockIndex {
new: cur.block_index(),
last: prev_bi,
});
}
if cur.source_epoch() != prev_dest {
return Err(HandoverChainError::SourceEpochMismatch {
last_dest: prev_dest,
got: cur.source_epoch(),
});
}
let Some(next_n) = prev_dest.as_u64().checked_add(1) else {
return Err(HandoverChainError::DestEpochOverflow);
};
let expected_dest = EpochIdentifier::new(next_n);
if cur.dest_epoch() != expected_dest {
if cur.dest_epoch().as_u64() < expected_dest.as_u64() {
return Err(HandoverChainError::DestEpochRegression {
last_dest: prev_dest,
expected: expected_dest,
got: cur.dest_epoch(),
});
}
return Err(HandoverChainError::DestEpochSkipped {
last_dest: prev_dest,
expected: expected_dest,
got: cur.dest_epoch(),
});
}
prev_bi = cur.block_index();
prev_dest = cur.dest_epoch();
prev_hash = cur.record_digest();
}
Ok(())
}
#[cfg(any(test, feature = "testing"))]
pub fn new_from_genesis(
genesis_certificate: HandoverCertificate,
) -> Result<Self, HandoverChainError> {
if !genesis_certificate.handover_record().is_genesis() {
return Err(HandoverChainError::InvalidGenesisFirstRecord);
}
genesis_certificate.validate_attestation_consistency()?;
let chain = Self(vec![genesis_certificate]);
if !chain[0].is_complete(&chain) {
return Err(HandoverChainError::IncompleteGenesisCertificate);
}
chain.validate()?;
Ok(chain)
}
pub fn insert_next_handover(
&mut self,
record: HandoverRecord,
) -> Result<(), HandoverChainError> {
debug_assert!(
self.validate().is_ok(),
"chain invariant violated before insert"
);
if self.0.is_empty() {
validate_canonical_genesis_first(&record)?;
self.0.push(HandoverCertificate::new(record));
return Ok(());
}
let last = self.0.last().expect("non-empty");
let last_rec = last.handover_record();
let expected_hash = last_rec.record_digest();
if *record.previous_handover_hash() != expected_hash {
return Err(HandoverChainError::PreviousHashMismatch {
expected: expected_hash,
got: *record.previous_handover_hash(),
});
}
let last_bi = last_rec.block_index();
if record.block_index() <= last_bi {
return Err(HandoverChainError::NonAscendingBlockIndex {
new: record.block_index(),
last: last_bi,
});
}
let last_dest = last_rec.dest_epoch();
if record.source_epoch() != last_dest {
return Err(HandoverChainError::SourceEpochMismatch {
last_dest,
got: record.source_epoch(),
});
}
let Some(next_n) = last_dest.as_u64().checked_add(1) else {
return Err(HandoverChainError::DestEpochOverflow);
};
let expected_dest = EpochIdentifier::new(next_n);
if record.dest_epoch() != expected_dest {
if record.dest_epoch().as_u64() < expected_dest.as_u64() {
return Err(HandoverChainError::DestEpochRegression {
last_dest,
expected: expected_dest,
got: record.dest_epoch(),
});
}
return Err(HandoverChainError::DestEpochSkipped {
last_dest,
expected: expected_dest,
got: record.dest_epoch(),
});
}
self.0.push(HandoverCertificate::new(record));
Ok(())
}
pub fn iter(&self) -> impl Iterator<Item = &HandoverCertificate> {
self.0.iter()
}
pub fn last(&self) -> Option<&HandoverCertificate> {
self.0.last()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter_after_epoch(
&self,
after_epoch: EpochIdentifier,
) -> impl Iterator<Item = &HandoverCertificate> + '_ {
debug_assert!(
self.validate().is_ok(),
"chain invariant violated in iter_after_epoch"
);
let start = after_epoch.as_u64().saturating_add(1);
let start = usize::try_from(start).unwrap_or(usize::MAX);
self.0.iter().skip(start)
}
pub fn certificate_for_epoch(
&self,
epoch: EpochIdentifier,
) -> Result<Option<&HandoverCertificate>, HandoverChainError> {
self.validate()?;
Ok(self
.0
.iter()
.find(|cert| cert.handover_record().dest_epoch() == epoch))
}
pub fn certificate_for_epoch_mut(
&mut self,
epoch: EpochIdentifier,
) -> Result<Option<&mut HandoverCertificate>, HandoverChainError> {
self.validate()?;
Ok(self
.0
.iter_mut()
.find(|cert| cert.handover_record().dest_epoch() == epoch))
}
pub fn certificate_for_block(
&self,
block_height: u64,
) -> Result<Option<&HandoverCertificate>, HandoverChainError> {
self.validate()?;
let mut result = None;
for cert in self.0.iter().skip(1) {
if block_height >= cert.handover_record().block_index() {
result = Some(cert);
} else {
break;
}
}
Ok(result)
}
pub fn epoch_for_block(
&self,
block_height: u64,
) -> Result<Option<EpochIdentifier>, HandoverChainError> {
Ok(self
.certificate_for_block(block_height)?
.map(|cert| cert.handover_record().dest_epoch()))
}
#[cfg(test)]
pub(crate) fn from_vec_unchecked_for_test(inner: Vec<HandoverCertificate>) -> Self {
Self(inner)
}
}
impl Index<usize> for HandoverChain {
type Output = HandoverCertificate;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
impl Deref for HandoverChain {
type Target = [HandoverCertificate];
fn deref(&self) -> &Self::Target {
&self.0
}
}