pub use trust_tasks_rs::specs::vta::backup::{
finalize_import::v1_1 as finalize_import_1_1, get_chunk::v1_0 as get_chunk,
initiate_export::v1_1 as initiate_export_1_1, initiate_import::v1_1 as initiate_import_1_1,
put_chunk::v1_0 as put_chunk,
};
pub const ALGORITHM_CHUNKED: &str = "chunkedTrustTask";
pub const ALGORITHM_STREAM: &str = "stream";
pub const MAX_CHUNK_SIZE: u64 = 262_144;
pub const MIN_CHUNK_SIZE: u64 = 16_384;
pub const MAX_CHUNK_COUNT: u64 = 4_096;
pub fn chunk_count(size: u64, chunk_size: u64) -> Option<u64> {
if !(MIN_CHUNK_SIZE..=MAX_CHUNK_SIZE).contains(&chunk_size) || size == 0 {
return None;
}
let count = size.div_ceil(chunk_size);
(count <= MAX_CHUNK_COUNT).then_some(count)
}
pub fn chunk_range(size: u64, chunk_size: u64, index: u64) -> Option<(u64, u64)> {
let count = chunk_count(size, chunk_size)?;
if index >= count {
return None;
}
let start = index * chunk_size;
Some((start, (start + chunk_size).min(size)))
}
pub fn sha256_digest_multibase(digest: &[u8; 32]) -> String {
let mut mh = Vec::with_capacity(34);
mh.extend_from_slice(&[0x12, 0x20]);
mh.extend_from_slice(digest);
multibase::encode(multibase::Base::Base58Btc, mh)
}
pub fn sha256_from_digest_multibase(value: &str) -> Option<[u8; 32]> {
let (_base, bytes) = multibase::decode(value).ok()?;
match bytes.split_at_checked(2) {
Some(([0x12, 0x20], digest)) => digest.try_into().ok(),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counts_follow_the_ceiling_formula() {
assert_eq!(chunk_count(524_300, MAX_CHUNK_SIZE), Some(3));
assert_eq!(
chunk_range(524_300, MAX_CHUNK_SIZE, 2),
Some((524_288, 524_300))
);
assert_eq!(chunk_range(524_300, MAX_CHUNK_SIZE, 3), None);
assert_eq!(chunk_count(MAX_CHUNK_SIZE, MAX_CHUNK_SIZE), Some(1));
}
#[test]
fn out_of_bound_sizes_are_refused_not_clamped() {
assert_eq!(chunk_count(10, MAX_CHUNK_SIZE + 1), None);
assert_eq!(chunk_count(10, MIN_CHUNK_SIZE - 1), None);
assert_eq!(chunk_count(0, MAX_CHUNK_SIZE), None);
assert_eq!(
chunk_count(MAX_CHUNK_COUNT * MAX_CHUNK_SIZE + 1, MAX_CHUNK_SIZE),
None
);
}
#[cfg(feature = "client")]
#[test]
fn digest_round_trips_and_matches_the_spec_example() {
use sha2::{Digest, Sha256};
let digest: [u8; 32] = Sha256::digest(b"backup-tail!").into();
let encoded = sha256_digest_multibase(&digest);
assert_eq!(encoded, "zQmTcWLvAPe4Txz32vVqZe5jgX4nBPiaBsTTCp4bLXDMzTT");
assert_eq!(sha256_from_digest_multibase(&encoded), Some(digest));
}
#[test]
fn a_non_sha256_multihash_is_unverifiable() {
let mut mh = vec![0x13, 0x40];
mh.extend_from_slice(&[0u8; 64]);
let encoded = multibase::encode(multibase::Base::Base58Btc, mh);
assert_eq!(sha256_from_digest_multibase(&encoded), None);
assert_eq!(sha256_from_digest_multibase("not-multibase"), None);
}
}