use alloc::vec::Vec;
use alloy_signer::SignerSync;
use nectar_postage::{Stamp, StampDigest};
use nectar_primitives::{Chunk, SingleOwnerChunk};
use thiserror::Error;
use crate::snapshot::{PersistPlan, Snapshot};
#[derive(Debug, Error)]
pub enum SealError {
#[error(transparent)]
Signer(#[from] alloy_signer::Error),
#[error(transparent)]
Chunk(#[from] nectar_primitives::PrimitivesError),
#[error("sealed chunk address does not match plan")]
AddressMismatch,
#[error("seal timestamp {timestamp} does not exceed previous seal timestamp {previous}")]
NonIncreasingTimestamp {
timestamp: u64,
previous: u64,
},
}
#[derive(Debug, Clone)]
pub struct SealedChunk {
pub chunk: SingleOwnerChunk,
pub stamp: Stamp,
}
#[must_use = "the sealed chunks are the snapshot to upload; dropping them discards the seal"]
pub fn seal_plan(
snapshot: &mut Snapshot,
plan: &PersistPlan,
timestamp: u64,
signer: &impl SignerSync,
) -> core::result::Result<Vec<SealedChunk>, SealError> {
if let Some(previous) = snapshot.last_seal_timestamp()
&& timestamp <= previous
{
return Err(SealError::NonIncreasingTimestamp {
timestamp,
previous,
});
}
let sealed = plan
.chunks
.iter()
.map(|planned| {
let chunk = SingleOwnerChunk::new(planned.id, planned.payload.clone(), signer)?;
if *chunk.address() != planned.address {
return Err(SealError::AddressMismatch);
}
let digest = StampDigest::new(
planned.address,
plan.batch_id,
planned.stamp_index,
timestamp,
);
let signature = signer.sign_message_sync(digest.to_prehash().as_slice())?;
let stamp = Stamp::new(
plan.batch_id,
planned.stamp_index.bucket(),
planned.stamp_index.index(),
timestamp,
signature,
);
Ok(SealedChunk { chunk, stamp })
})
.collect::<core::result::Result<Vec<_>, SealError>>()?;
snapshot.record_seal_timestamp(timestamp);
Ok(sealed)
}