use super::checkpoint::SignedCheckpoint;
use super::note::KeyName;
use super::tiles::TileId;
use crate::error::{Error, IntegrityError, IntegrityKind, QueryError};
use crate::log::{BalanceTree, Hash, InclusionProof, SealedBalanceProof, TreeHead};
use crate::model::{
AccountHandle, Balance, Currency, Label, Layer, LedgerId, PeriodId, Record, Seal,
};
pub(crate) fn period_label(text: &str) -> Result<PeriodId, Error> {
Label::new(text).map_err(|_| {
QueryError::InvalidLabel {
text: text.to_owned(),
}
.into()
})
}
pub(crate) fn claimed_head(
ledger: &LedgerId,
checkpoint: &SignedCheckpoint,
size: u64,
) -> Result<TreeHead, Error> {
let origin = checkpoint.checkpoint().origin();
if origin != &KeyName::from(ledger) {
return Err(super::Error::ForeignCheckpoint {
origin: origin.clone(),
}
.into());
}
let head = checkpoint.checkpoint().head();
if head.size > size {
return Err(super::Error::CheckpointMismatch.into());
}
Ok(head)
}
pub(crate) fn same_root(claimed: TreeHead, actual: TreeHead) -> Result<(), Error> {
if claimed == actual {
Ok(())
} else {
Err(super::Error::CheckpointMismatch.into())
}
}
pub(crate) fn seal_record<const P: u8>(bytes: &[u8], position: u64) -> Result<Seal<P>, Error> {
match Record::<P>::decode(bytes) {
Ok(Record::Seal(seal)) => Ok(seal),
_ => Err(IntegrityError::new(IntegrityKind::Undecodable, Some(position)).into()),
}
}
pub(crate) struct SealedParts {
pub(crate) seal_leaf: Vec<u8>,
pub(crate) seal_proof: InclusionProof,
pub(crate) account_leaf: Vec<u8>,
pub(crate) account_proof: InclusionProof,
pub(crate) handle: AccountHandle,
pub(crate) currency: Currency,
pub(crate) layer: Layer,
}
pub(crate) fn sealed_balance_proof<const P: u8>(
parts: SealedParts,
seal: &Seal<P>,
leaves: impl IntoIterator<Item = (AccountHandle, Balance<P>)>,
head: &TreeHead,
) -> Result<SealedBalanceProof<P>, Error> {
let position = parts.seal_proof.index;
let mismatch = || {
Error::from(IntegrityError::new(
IntegrityKind::SealMismatch,
Some(position),
))
};
let balance = match seal.tree(parts.currency, parts.layer) {
None => None,
Some(sealed) => {
let mut tree = BalanceTree::new(parts.currency, parts.layer, seal.parts().accounts);
for (handle, balance) in leaves {
tree.set(handle, balance).map_err(|_| mismatch())?;
}
if tree.root() != sealed.root {
return Err(mismatch());
}
Some(tree.prove(parts.handle).map_err(|_| mismatch())?)
}
};
let proof = SealedBalanceProof {
seal_leaf: parts.seal_leaf,
seal_proof: parts.seal_proof,
account_leaf: parts.account_leaf,
account_proof: parts.account_proof,
currency: parts.currency,
layer: parts.layer,
handle: parts.handle,
balance,
};
proof.verify(head).map_err(|_| mismatch())?;
Ok(proof)
}
pub(crate) fn tile_beyond(tile: &TileId, size: u64) -> Error {
QueryError::PositionBeyondHead {
position: tile.needs_size(),
size,
}
.into()
}
pub(crate) fn tile_bytes(hashes: &[Hash]) -> Vec<u8> {
hashes.iter().flat_map(Hash::as_bytes).copied().collect()
}