#![cfg(all(feature = "compression", feature = "checksum", feature = "messagepack"))]
use cachekit_core::byte_storage::ByteStorageError;
use cachekit_core::{ByteStorage, StorageEnvelope};
use sha2::{Digest, Sha256};
const FIXTURE: &str = include_str!("vectors/decode-bounds.json");
const FIXTURE_SHA256: &str = "907b025d2b270a0f60abd9296a8a1c864e69057c553ac7a70206b44256558916";
const PRE_SCAN: &str = "decode pre-scan: ";
const MAX_DEPTH: usize = 100;
#[derive(serde::Deserialize)]
struct Fixture {
version: String,
reject_vectors: Vec<Vector>,
accept_vectors: Vec<Vector>,
}
#[derive(serde::Deserialize)]
struct Vector {
name: String,
input_hex: String,
input_len: usize,
#[serde(default)]
reject_reasons: Vec<String>,
}
impl Vector {
fn input(&self) -> Vec<u8> {
let bytes = hex::decode(&self.input_hex).expect("input_hex must be hex");
assert_eq!(bytes.len(), self.input_len, "[{}] input_len", self.name);
bytes
}
}
fn load_fixture() -> Fixture {
serde_json::from_str(FIXTURE).expect("decode-bounds.json fixture must parse")
}
fn pre_scan_message(result: &Result<(Vec<u8>, String), ByteStorageError>) -> Option<&str> {
match result {
Err(ByteStorageError::DeserializationFailed(msg)) => msg.strip_prefix(PRE_SCAN),
_ => None,
}
}
#[test]
fn fixture_integrity_pinned_sha256() {
let digest = hex::encode(Sha256::digest(FIXTURE.as_bytes()));
assert_eq!(
digest, FIXTURE_SHA256,
"vendored decode-bounds.json drifted from its pinned sha256 — \
re-vendor from the protocol repo and update FIXTURE_SHA256 deliberately"
);
}
#[test]
fn fixture_is_current_version_with_vectors() {
let fixture = load_fixture();
assert_eq!(fixture.version, "1.1.0");
assert_eq!(fixture.reject_vectors.len(), 17);
assert_eq!(fixture.accept_vectors.len(), 3);
}
#[test]
fn every_reject_vector_is_rejected_by_the_pre_scan() {
let storage = ByteStorage::new(None);
for vector in load_fixture().reject_vectors {
let result = storage.retrieve(&vector.input());
let reason = pre_scan_message(&result).unwrap_or_else(|| {
panic!("[{}] not rejected by the pre-scan: {result:?}", vector.name)
});
match vector.reject_reasons.as_slice() {
[r] if r == "depth" => assert_eq!(
reason,
format!("nests deeper than {MAX_DEPTH} levels"),
"[{}]",
vector.name
),
[r] if r == "overclaim" => {
assert!(
reason.starts_with("declares more "),
"[{}] {reason}",
vector.name
)
}
_ => {}
}
assert!(!storage.validate(&vector.input()), "[{}]", vector.name);
}
}
#[test]
fn every_accept_vector_passes_the_pre_scan() {
let storage = ByteStorage::new(None);
for vector in load_fixture().accept_vectors {
let result = storage.retrieve(&vector.input());
assert_eq!(
pre_scan_message(&result),
None,
"[{}] {result:?}",
vector.name
);
}
}
fn map_form_envelope_nested(storage: &ByteStorage, payload: &[u8], depth: usize) -> Vec<u8> {
let envelope: StorageEnvelope =
rmp_serde::from_slice(&storage.store(payload, None).unwrap()).unwrap();
let mut doc = rmp_serde::to_vec_named(&envelope).unwrap();
assert_eq!(
doc[0], 0x84,
"to_vec_named must emit a fixmap of the 4 fields"
);
doc[0] = 0x85;
doc.push(0xa0); doc.extend(std::iter::repeat_n(0x91, depth - 1));
doc.push(0xc0);
doc
}
#[test]
fn complete_envelope_at_the_depth_bound_decodes_on_small_stacks() {
for stack in [2 << 20, 1 << 20] {
let result = std::thread::Builder::new()
.stack_size(stack)
.spawn(|| {
let storage = ByteStorage::new(None);
let at_bound = map_form_envelope_nested(&storage, b"at the bound", MAX_DEPTH);
let past_bound = map_form_envelope_nested(&storage, b"at the bound", MAX_DEPTH + 1);
(storage.retrieve(&at_bound), storage.retrieve(&past_bound))
})
.unwrap()
.join()
.expect("retrieve panicked");
let (at_bound, past_bound) = result;
assert_eq!(
at_bound,
Ok((b"at the bound".to_vec(), "msgpack".to_owned())),
"stack {stack}"
);
assert_eq!(
pre_scan_message(&past_bound),
Some(format!("nests deeper than {MAX_DEPTH} levels").as_str()),
"stack {stack}"
);
}
}