use super::{
parts::extract_metadata_owned,
proof::{build_proof_data, data_hash_state, finish_with},
ResourceAction, ResourceError,
};
use crate::{
buffer::types::{Compressor, DecompressError},
constants::RESOURCE_RANDOM_HASH_SIZE,
};
use alloc::{boxed::Box, sync::Arc, vec, vec::Vec};
use rns_crypto::sha256::Sha256;
#[derive(Clone, Debug)]
pub struct AssemblyId(Arc<()>);
impl AssemblyId {
pub(super) fn new() -> Self {
Self(Arc::new(()))
}
}
impl PartialEq for AssemblyId {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for AssemblyId {}
pub struct ResourceAssembly {
pub(super) id: AssemblyId,
pub(super) data: AssemblyData,
}
pub struct AssemblyResult {
pub(super) id: AssemblyId,
pub(super) result: Result<Vec<ResourceAction>, ResourceError>,
}
fn decompress_error(error: DecompressError) -> ResourceError {
match error {
DecompressError::TooLarge => ResourceError::TooLarge,
DecompressError::InvalidData => ResourceError::DecompressionFailed,
}
}
impl ResourceAssembly {
pub fn id(&self) -> &AssemblyId {
&self.id
}
pub fn is_compressed(&self) -> bool {
self.data.compressed
}
pub fn take_compressed_data(&mut self) -> Vec<u8> {
core::mem::take(&mut self.data.plaintext)
}
pub fn finish_decompressed(
self,
decompressed: Vec<u8>,
data_state: Option<Sha256>,
) -> AssemblyResult {
AssemblyResult {
id: self.id,
result: self.data.finish(decompressed, data_state),
}
}
pub fn decompression_failed(self, too_large: bool) -> AssemblyResult {
AssemblyResult {
id: self.id,
result: Err(decompress_error(if too_large {
DecompressError::TooLarge
} else {
DecompressError::InvalidData
})),
}
}
pub fn input_capacity(&self) -> usize {
self.data.plaintext.capacity()
}
pub fn max_output_size(&self) -> usize {
self.data.limit
}
pub fn run(self, compressor: &dyn Compressor) -> AssemblyResult {
AssemblyResult {
id: self.id,
result: self.data.run(compressor),
}
}
}
pub(super) struct AssemblyData {
pub plaintext: Vec<u8>,
pub data_state: Option<Box<Sha256>>,
pub random_hash: [u8; RESOURCE_RANDOM_HASH_SIZE],
pub resource_hash: [u8; 32],
pub compressed: bool,
pub metadata: bool,
pub app_flag: bool,
pub limit: usize,
}
impl AssemblyData {
pub fn run(
mut self,
compressor: &dyn Compressor,
) -> Result<Vec<ResourceAction>, ResourceError> {
let plaintext = core::mem::take(&mut self.plaintext);
let (decompressed, data_state) = if self.compressed {
let decompressed = compressor
.decompress_bounded(&plaintext, self.limit)
.map_err(decompress_error)?;
(decompressed, None)
} else {
(plaintext, self.data_state.take().map(|state| *state))
};
self.finish(decompressed, data_state)
}
fn finish(
self,
decompressed: Vec<u8>,
data_state: Option<Sha256>,
) -> Result<Vec<ResourceAction>, ResourceError> {
let data_state = data_state.unwrap_or_else(|| data_hash_state(&decompressed));
let hash = finish_with(&data_state, &self.random_hash);
if hash != self.resource_hash {
return Err(ResourceError::HashMismatch);
}
let proof = build_proof_data(&hash, &finish_with(&data_state, &hash));
let (data, metadata) = if self.metadata {
let (metadata, data) =
extract_metadata_owned(decompressed).ok_or(ResourceError::InvalidPart)?;
(data, Some(metadata))
} else {
(decompressed, None)
};
Ok(vec![
ResourceAction::SendProof(proof),
ResourceAction::DataReceived {
data,
metadata,
app_flag: self.app_flag,
},
ResourceAction::Completed,
])
}
}