From e50a5ced65a1f03ebdbe5b0d3b09a57b11b7edbe Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 7 Nov 2026 01:40:44 +0100 Subject: refuse misplaced authors --- src/document/testing.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'src/document/testing.rs') diff --git a/src/document/testing.rs b/src/document/testing.rs index 7b75a96..cd6c15b 100644 --- a/src/document/testing.rs +++ b/src/document/testing.rs @@ -29,3 +29,48 @@ pub(crate) fn try_change( } out } + +/// `raw` (an uncompressed change chunk, as `Change::raw_bytes` gives one) with an author +/// footer appended to its extra bytes, its checksum made right again: what no honest +/// node sends for a change that isn't its actor's first (`changes::misplaced_author`). +pub(crate) fn with_author_footer(raw: &[u8], author: &[u8]) -> Vec { + use sha2::{Digest, Sha256}; + fn leb(mut n: usize, out: &mut Vec) { + loop { + let byte = (n & 0x7f) as u8; + n >>= 7; + if n == 0 { + out.push(byte); + return; + } + out.push(byte | 0x80); + } + } + assert_eq!(raw[8], 1, "an uncompressed change chunk"); + let (mut length, mut read, mut shift) = (0usize, 0, 0); + loop { + let byte = raw[9 + read]; + length |= usize::from(byte & 0x7f) << shift; + read += 1; + if byte & 0x80 == 0 { + break; + } + shift += 7; + } + let mut body = raw[9 + read..9 + read + length].to_vec(); + body.push(1); + leb(author.len(), &mut body); + body.extend_from_slice(author); + let mut header = Vec::new(); + leb(body.len(), &mut header); + let mut summed = vec![1u8]; + summed.extend(&header); + summed.extend(&body); + let sum = Sha256::digest(&summed); + let mut out = raw[..4].to_vec(); + out.extend(&sum[..4]); + out.push(1); + out.extend(header); + out.extend(body); + out +} -- cgit v1.3.1