use dig_nat::{
RangeFrame, MAX_CHUNK_LENS_PER_FRAME, MAX_FIRST_FRAME_CHUNK_LENS, MAX_FRAMED_BODY,
MAX_INCLUSION_PROOF_B64, MAX_RANGE_FRAME_PAYLOAD,
};
use tokio::io::AsyncWriteExt;
fn data_frame(offset: u64, payload: Vec<u8>) -> RangeFrame {
RangeFrame::data(offset, payload)
}
fn first_frame(payload: Vec<u8>, chunk_count: usize, proof_len: usize) -> RangeFrame {
RangeFrame::data(u64::MAX, payload)
.with_declared_length(u64::MAX)
.with_identity("ab".repeat(32), u64::MAX, u64::MAX)
.with_chunk_lens_page(u64::MAX, vec![262_144; chunk_count])
.with_chunk_index(u64::MAX)
.with_inclusion_proof("A".repeat(proof_len))
}
fn body_len(frame: &RangeFrame) -> usize {
serde_json::to_vec(frame)
.expect("a range frame serializes")
.len()
}
fn expect_refusal(encoded: std::io::Result<Vec<u8>>, what: &str) -> std::io::Error {
match encoded {
Ok(wire) => panic!(
"{what} must be refused at the sender, but encoded {} bytes",
wire.len()
),
Err(e) => e,
}
}
async fn decode_all(wire: Vec<u8>) -> std::io::Result<Vec<RangeFrame>> {
let mut cursor = std::io::Cursor::new(wire);
let mut frames = Vec::new();
while let Some(frame) = RangeFrame::decode(&mut cursor).await? {
frames.push(frame);
}
Ok(frames)
}
#[tokio::test]
async fn encode_refuses_a_payload_above_the_published_ceiling() {
let over = data_frame(0, vec![7u8; MAX_RANGE_FRAME_PAYLOAD + 1]);
let err = expect_refusal(over.encode(), "a payload over the ceiling");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
let msg = err.to_string();
assert!(
msg.contains(&MAX_RANGE_FRAME_PAYLOAD.to_string()),
"the error must name the ceiling so the sender knows what to split on, got: {msg}"
);
}
#[tokio::test]
async fn the_published_chunk_bound_fits_with_every_field_at_its_maximum() {
let frame = first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
MAX_FIRST_FRAME_CHUNK_LENS,
MAX_INCLUSION_PROOF_B64,
);
let budget = body_len(&frame);
assert!(
budget <= MAX_FRAMED_BODY,
"at the published bound of {MAX_FIRST_FRAME_CHUNK_LENS} entries the worst-case body is {budget} B, over the {MAX_FRAMED_BODY} B cap — the constant is too generous, which is the UNSAFE direction: a conforming sender would emit a frame the receiver must reject"
);
let wire = frame.encode().expect("the bound must be encodable today");
let decoded = decode_all(wire).await.expect("the receiver must accept it");
assert_eq!(decoded, vec![frame]);
}
#[tokio::test]
async fn one_chunk_entry_past_the_published_bound_does_not_fit() {
let frame = first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
MAX_FIRST_FRAME_CHUNK_LENS + 1,
MAX_INCLUSION_PROOF_B64,
);
let budget = body_len(&frame);
assert!(
budget > MAX_FRAMED_BODY,
"one entry past the published bound still fits at {budget} B — the constant is lower than the real ceiling, so it is not the bound it claims to be"
);
}
#[tokio::test]
async fn a_permitted_256_mebibyte_module_has_no_conforming_first_frame_yet() {
let chunks_in_256_mib = 256 * 1024 / 64; assert!(chunks_in_256_mib > MAX_FIRST_FRAME_CHUNK_LENS);
expect_refusal(
first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
chunks_in_256_mib,
MAX_INCLUSION_PROOF_B64,
)
.encode(),
"a 256 MiB module's first frame at the payload ceiling",
);
expect_refusal(
first_frame(Vec::new(), 9_500, MAX_INCLUSION_PROOF_B64).encode(),
"a first frame whose metadata alone exceeds the body cap",
);
}
#[tokio::test]
async fn a_one_mebibyte_resource_rides_ceiling_sized_frames_over_a_real_stream() {
let resource: Vec<u8> = (0..1024u32 * 1024).map(|i| (i % 251) as u8).collect();
let (mut writer, reader) = tokio::io::duplex(4 * 1024 * 1024);
let mut offset = 0usize;
let mut frames_written = 0usize;
while offset < resource.len() {
let end = (offset + MAX_RANGE_FRAME_PAYLOAD).min(resource.len());
let mut frame = data_frame(offset as u64, resource[offset..end].to_vec());
frame.complete = end == resource.len();
writer
.write_all(&frame.encode().expect("a ceiling-sized frame encodes"))
.await
.unwrap();
frames_written += 1;
offset = end;
}
writer.shutdown().await.unwrap();
assert!(frames_written > 1, "1 MiB must need more than one frame");
let mut reader = reader;
let mut reassembled = Vec::new();
while let Some(frame) = RangeFrame::decode(&mut reader).await.unwrap() {
assert_eq!(frame.offset as usize, reassembled.len());
reassembled.extend_from_slice(&frame.bytes);
}
assert_eq!(reassembled, resource);
}
#[tokio::test]
async fn encode_refuses_a_legal_payload_whose_metadata_overflows_the_body() {
let mut frame = data_frame(0, vec![1u8; MAX_RANGE_FRAME_PAYLOAD]);
frame.chunk_lens = Some((0..MAX_FRAMED_BODY as u64).map(|i| 1_000_000 + i).collect());
let err = expect_refusal(frame.encode(), "a body over the decode cap");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
}
#[tokio::test]
async fn encode_refuses_an_inclusion_proof_above_the_published_cap() {
let entries = MAX_FIRST_FRAME_CHUNK_LENS / 2;
let over = first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
entries,
MAX_INCLUSION_PROOF_B64 + 1,
);
let err = expect_refusal(over.encode(), "an inclusion proof over the published cap");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
let msg = err.to_string();
assert!(
msg.contains("MAX_INCLUSION_PROOF_B64"),
"the error must name the cap it enforces so the premise is traceable, got: {msg}"
);
}
#[tokio::test]
async fn a_proof_at_exactly_the_published_cap_still_fits_at_the_entry_bound() {
let at_cap = first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
MAX_FIRST_FRAME_CHUNK_LENS,
MAX_INCLUSION_PROOF_B64,
);
assert_eq!(
at_cap.inclusion_proof.as_ref().map(String::len),
Some(MAX_INCLUSION_PROOF_B64),
"the fixture must hold the proof AT its cap — a helper that silently shrinks a co-occurring field measures a narrower frame than the protocol permits"
);
at_cap
.encode()
.expect("a proof of exactly MAX_INCLUSION_PROOF_B64 is legal — the cap is inclusive");
}
#[tokio::test]
async fn the_paging_threshold_leaves_measured_margin_below_the_body_cap() {
let paged = first_frame(
vec![9u8; MAX_RANGE_FRAME_PAYLOAD],
MAX_CHUNK_LENS_PER_FRAME,
MAX_INCLUSION_PROOF_B64,
);
let body = body_len(&paged);
assert!(
body <= MAX_FRAMED_BODY,
"a full prologue page at every maximum is {body} B, over the {MAX_FRAMED_BODY} B cap — the paging threshold a serve path splits on does not itself fit"
);
assert_eq!(
body, 62_470,
"the published margin figure moved; re-derive it and re-publish it in SPEC.md rather than updating this number to match"
);
}