extern crate alloc;
use alloc::vec::Vec;
use thiserror::Error;
use super::seal::{SEAL_WIRE_MIN_LEN, SealDecodeBudget, SealDecodeError, SealRecord};
use super::shared::read_u32;
const LINEAGE_PROOF_WIRE_V1: u8 = 0x01;
const LINEAGE_PROOF_WIRE_V2: u8 = 0x02;
const LINEAGE_PROOF_WIRE_HEADER_LEN: usize = 5;
const LINEAGE_NODE_LEN: usize = 32;
const LINEAGE_PROOF_ENTRY_MIN_LEN: usize = LINEAGE_NODE_LEN + SEAL_WIRE_MIN_LEN;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LineageProofDecodeBudget {
generations: usize,
candidates: usize,
joiners: usize,
}
impl LineageProofDecodeBudget {
#[must_use]
pub const fn new(max_generations: usize, max_candidates: usize) -> Self {
Self {
generations: max_generations,
candidates: max_candidates,
joiners: SealDecodeBudget::new(0).max_joiners(),
}
}
#[must_use]
pub const fn with_max_joiners(self, max_joiners: usize) -> Self {
Self {
joiners: max_joiners,
..self
}
}
#[must_use]
pub const fn max_generations(self) -> usize {
self.generations
}
#[must_use]
pub const fn max_candidates(self) -> usize {
self.candidates
}
}
#[non_exhaustive]
#[derive(Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineageProofDecodeError {
#[error("unknown lineage-proof wire version: {0:#04x}")]
UnknownVersion(u8),
#[error("lineage-proof v1 embeds an admission-bearing seal at generation {generation}")]
AdmissionInVersionOne {
generation: u64,
},
#[error("lineage-proof v2 carries no admission-bearing seal")]
VersionTwoWithoutAdmissions,
#[error("unexpected lineage-proof frame length: expected {expected}, found {found}")]
UnexpectedLength {
expected: usize,
found: usize,
},
#[error("lineage proof declares {count} generations, past the budget's {budget}")]
TooManyGenerations {
count: u64,
budget: u64,
},
#[error("lineage proof generations not consecutive: {found} after {previous}")]
NonConsecutiveGenerations {
previous: u64,
found: u64,
},
#[error("lineage proof seal entry: {0}")]
Seal(#[from] SealDecodeError),
}
const fn consecutive(
previous: Option<u64>,
generation: u64,
) -> Result<(), LineageProofDecodeError> {
match previous {
None => Ok(()),
Some(previous) => match previous.checked_add(1) {
Some(next) if next == generation => Ok(()),
_ => Err(LineageProofDecodeError::NonConsecutiveGenerations {
previous,
found: generation,
}),
},
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LineageProofEntry {
seal: SealRecord,
node: [u8; LINEAGE_NODE_LEN],
}
impl LineageProofEntry {
#[must_use]
pub const fn new(seal: SealRecord, node: [u8; LINEAGE_NODE_LEN]) -> Self {
Self { seal, node }
}
#[must_use]
pub const fn seal(&self) -> &SealRecord {
&self.seal
}
#[must_use]
pub const fn node(&self) -> [u8; LINEAGE_NODE_LEN] {
self.node
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LineageProofRecord {
entries: Vec<LineageProofEntry>,
}
impl LineageProofRecord {
pub fn try_new(entries: Vec<LineageProofEntry>) -> Result<Self, LineageProofDecodeError> {
let mut previous: Option<u64> = None;
for entry in &entries {
let generation = entry.seal.declaration().generation();
consecutive(previous, generation)?;
previous = Some(generation);
}
Ok(Self { entries })
}
#[must_use]
pub fn entries(&self) -> &[LineageProofEntry] {
&self.entries
}
#[must_use]
pub const fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.entries.is_empty()
}
fn recorded(&self) -> bool {
self.entries
.iter()
.any(|entry| entry.seal.admission().is_some())
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(self.encoded_len());
out.push(if self.recorded() {
LINEAGE_PROOF_WIRE_V2
} else {
LINEAGE_PROOF_WIRE_V1
});
let count = u32::try_from(self.entries.len()).unwrap_or(u32::MAX);
out.extend_from_slice(&count.to_be_bytes());
for entry in &self.entries {
out.extend_from_slice(&entry.node);
out.extend_from_slice(&entry.seal.to_bytes());
}
out
}
#[must_use]
pub fn encoded_len(&self) -> usize {
self.entries
.iter()
.fold(LINEAGE_PROOF_WIRE_HEADER_LEN, |length, entry| {
length + LINEAGE_NODE_LEN + entry.seal.encoded_len()
})
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, LineageProofDecodeError> {
Self::decode(bytes, None)
}
pub fn from_bytes_with_budget(
bytes: &[u8],
budget: LineageProofDecodeBudget,
) -> Result<Self, LineageProofDecodeError> {
Self::decode(bytes, Some(budget))
}
fn decode(
bytes: &[u8],
budget: Option<LineageProofDecodeBudget>,
) -> Result<Self, LineageProofDecodeError> {
if bytes.len() < LINEAGE_PROOF_WIRE_HEADER_LEN {
return Err(LineageProofDecodeError::UnexpectedLength {
expected: LINEAGE_PROOF_WIRE_HEADER_LEN,
found: bytes.len(),
});
}
let version = bytes[0];
if version != LINEAGE_PROOF_WIRE_V1 && version != LINEAGE_PROOF_WIRE_V2 {
return Err(LineageProofDecodeError::UnknownVersion(version));
}
let recorded = version == LINEAGE_PROOF_WIRE_V2;
let (count, rest) =
read_u32(&bytes[1..]).ok_or(LineageProofDecodeError::UnexpectedLength {
expected: LINEAGE_PROOF_WIRE_HEADER_LEN,
found: bytes.len(),
})?;
if let Some(budget) = budget
&& u64::from(count) > budget.max_generations() as u64
{
return Err(LineageProofDecodeError::TooManyGenerations {
count: u64::from(count),
budget: budget.max_generations() as u64,
});
}
if count as usize > rest.len() / LINEAGE_PROOF_ENTRY_MIN_LEN {
let expected = (count as usize)
.saturating_mul(LINEAGE_PROOF_ENTRY_MIN_LEN)
.saturating_add(LINEAGE_PROOF_WIRE_HEADER_LEN);
return Err(LineageProofDecodeError::UnexpectedLength {
expected,
found: bytes.len(),
});
}
let mut entries = Vec::with_capacity(count as usize);
let mut previous: Option<u64> = None;
let mut rest = rest;
for _ in 0..count {
let node: [u8; LINEAGE_NODE_LEN] = rest
.get(..LINEAGE_NODE_LEN)
.and_then(|node| node.try_into().ok())
.ok_or(LineageProofDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len() + LINEAGE_PROOF_ENTRY_MIN_LEN,
found: bytes.len(),
})?;
rest = &rest[LINEAGE_NODE_LEN..];
let (seal, tail) = SealRecord::from_prefix_with_budget(
rest,
budget.map(|budget| {
SealDecodeBudget::new(budget.max_candidates()).with_max_joiners(budget.joiners)
}),
)?;
rest = tail;
if !recorded && seal.admission().is_some() {
return Err(LineageProofDecodeError::AdmissionInVersionOne {
generation: seal.declaration().generation(),
});
}
let generation = seal.declaration().generation();
consecutive(previous, generation)?;
previous = Some(generation);
entries.push(LineageProofEntry { seal, node });
}
if recorded && !entries.iter().any(|entry| entry.seal.admission().is_some()) {
return Err(LineageProofDecodeError::VersionTwoWithoutAdmissions);
}
if rest.is_empty() {
Ok(Self { entries })
} else {
Err(LineageProofDecodeError::UnexpectedLength {
expected: bytes.len() - rest.len(),
found: bytes.len(),
})
}
}
}