use alloc::vec::Vec;
use super::{AdviceMap, MastForest, MastForestError, serialization};
use crate::serde::{BudgetedReader, ByteReader, DeserializationError, SliceReader};
#[derive(Debug, Clone)]
pub struct UntrustedMastForest {
pub(super) bytes: Vec<u8>,
pub(super) layout: serialization::ForestLayout,
pub(super) advice_map: AdviceMap,
pub(super) remaining_allocation_budget: Option<usize>,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UntrustedMastForestReadOptions {
wire_byte_budget: Option<usize>,
validation_allocation_budget: Option<usize>,
}
impl UntrustedMastForestReadOptions {
pub fn new() -> Self {
Self::default()
}
pub fn with_wire_byte_budget(mut self, budget: usize) -> Self {
self.wire_byte_budget = Some(budget);
self
}
#[cfg(test)]
pub(crate) fn with_validation_allocation_budget(mut self, budget: usize) -> Self {
self.validation_allocation_budget = Some(budget);
self
}
fn wire_byte_budget(self, bytes_len: usize) -> usize {
self.wire_byte_budget.unwrap_or(bytes_len)
}
fn validation_allocation_budget(self, wire_byte_budget: usize) -> usize {
self.validation_allocation_budget
.unwrap_or_else(|| serialization::default_untrusted_allocation_budget(wire_byte_budget))
}
}
impl UntrustedMastForest {
pub fn validate(self) -> Result<MastForest, MastForestError> {
let is_hashless = self.layout.is_hashless();
let forest = self.into_materialized().map_err(MastForestError::Deserialization)?;
if !is_hashless {
forest.validate_node_hashes()?;
}
forest.validate()?;
Ok(forest)
}
pub fn read_from_bytes(bytes: &[u8]) -> Result<Self, DeserializationError> {
Self::read_from_bytes_with_options(bytes, UntrustedMastForestReadOptions::default())
}
pub fn read_from_bytes_with_options(
bytes: &[u8],
options: UntrustedMastForestReadOptions,
) -> Result<Self, DeserializationError> {
let wire_byte_budget = options.wire_byte_budget(bytes.len());
let mut reader = BudgetedReader::new(SliceReader::new(bytes), wire_byte_budget);
let (forest, _flags) = serialization::read_untrusted_with_flags_and_allocation_budget(
&mut reader,
options.validation_allocation_budget(wire_byte_budget),
)?;
if reader.has_more_bytes() {
return Err(DeserializationError::InvalidValue(
"extra bytes after MastForest payload".into(),
));
}
Ok(forest)
}
}