use sha2::{Digest, Sha256};
const EDITION_LABEL: &[u8] = b"vector-community/v1/edition";
pub fn edition_signing_bytes(
entity_id: &[u8; 32],
version: u64,
prev_hash: Option<&[u8; 32]>,
content: &[u8],
) -> Vec<u8> {
let mut out = Vec::with_capacity(8 + EDITION_LABEL.len() + 32 + 8 + 1 + 32 + 8 + content.len());
out.extend_from_slice(&(EDITION_LABEL.len() as u64).to_be_bytes());
out.extend_from_slice(EDITION_LABEL);
out.extend_from_slice(entity_id);
out.extend_from_slice(&version.to_be_bytes());
match prev_hash {
Some(h) => {
out.push(1);
out.extend_from_slice(h);
}
None => {
out.push(0);
out.extend_from_slice(&[0u8; 32]);
}
}
out.extend_from_slice(&(content.len() as u64).to_be_bytes());
out.extend_from_slice(content);
out
}
pub fn edition_hash(
entity_id: &[u8; 32],
version: u64,
prev_hash: Option<&[u8; 32]>,
content: &[u8],
) -> [u8; 32] {
let mut h = Sha256::new();
h.update(edition_signing_bytes(entity_id, version, prev_hash, content));
h.finalize().into()
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Edition {
pub version: u64,
pub prev_hash: Option<[u8; 32]>,
pub self_hash: [u8; 32],
pub created_at: u64,
pub tiebreak_id: [u8; 32],
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FoldResult {
pub head: Option<usize>,
pub gap: bool,
pub anchored: bool,
}
pub fn fold(editions: &[Edition], floor: u64, floor_hash: Option<&[u8; 32]>) -> FoldResult {
use std::collections::BTreeMap;
let mut by_version: BTreeMap<u64, usize> = BTreeMap::new();
for (i, e) in editions.iter().enumerate() {
if e.version < floor {
continue;
}
match by_version.get(&e.version) {
Some(&j) => {
let cur = &editions[j];
if e.tiebreak_id < cur.tiebreak_id {
by_version.insert(e.version, i);
}
}
None => {
by_version.insert(e.version, i);
}
}
}
let versions: Vec<u64> = by_version.keys().copied().collect();
if versions.is_empty() {
return FoldResult { head: None, gap: false, anchored: false };
}
let lo = &editions[by_version[&versions[0]]];
let anchored = if floor == 0 {
versions[0] == 1 && lo.prev_hash.is_none()
} else if versions[0] == floor {
floor_hash == Some(&lo.self_hash)
} else if versions[0] == floor + 1 {
floor_hash.is_some() && lo.prev_hash.as_ref() == floor_hash
} else {
false };
let mut gap = !anchored;
let mut head_idx = by_version[&versions[0]];
for pair in versions.windows(2) {
let lo_idx = by_version[&pair[0]];
let hi_idx = by_version[&pair[1]];
let linked = pair[1] == pair[0] + 1
&& editions[hi_idx].prev_hash == Some(editions[lo_idx].self_hash);
if linked {
head_idx = hi_idx;
} else {
gap = true; break;
}
}
FoldResult { head: Some(head_idx), gap, anchored }
}
pub fn bootstrap_head(editions: &[Edition], floor: u64) -> Option<usize> {
let mut best: Option<usize> = None;
for (i, e) in editions.iter().enumerate() {
if e.version < floor {
continue; }
match best {
Some(b) => {
let cur = &editions[b];
let take = e.version > cur.version
|| (e.version == cur.version && e.tiebreak_id < cur.tiebreak_id);
if take {
best = Some(i);
}
}
None => best = Some(i),
}
}
best
}
#[cfg(test)]
mod tests {
use super::*;
fn id(b: u8) -> [u8; 32] {
[b; 32]
}
#[test]
fn edition_hash_golden_vector() {
let h = edition_hash(&id(0x11), 1, None, b"hello");
assert_eq!(
crate::simd::hex::bytes_to_hex_32(&h),
"2daf42e65a6bc259a4c99fac6df754a5d3d92310607cf13e2a1e8c94d42f6303"
);
}
#[test]
fn edition_hash_is_field_unambiguous() {
assert_ne!(
edition_hash(&id(1), 2, None, b"x"),
edition_hash(&id(1), 0, None, b"x"),
);
let with_prev = edition_hash(&id(1), 2, Some(&id(9)), b"x");
let without = edition_hash(&id(1), 2, None, b"x");
assert_ne!(with_prev, without, "prev presence changes the hash");
}
#[test]
fn contiguous_chain_folds_to_latest() {
let e1 = Edition { version: 1, prev_hash: None, self_hash: id(1), created_at: 100, tiebreak_id: id(0xa1) };
let e2 = Edition { version: 2, prev_hash: Some(id(1)), self_hash: id(2), created_at: 101, tiebreak_id: id(0xa2) };
let e3 = Edition { version: 3, prev_hash: Some(id(2)), self_hash: id(3), created_at: 102, tiebreak_id: id(0xa3) };
let r = fold(&[e1, e2, e3], 0, None);
assert_eq!(r, FoldResult { head: Some(2), gap: false, anchored: true });
}
#[test]
fn bootstrap_head_takes_highest_version_across_gaps() {
let ed = |v: u64| Edition {
version: v,
prev_hash: if v == 1 { None } else { Some(id(v as u8 - 1)) },
self_hash: id(v as u8),
created_at: 100 + v,
tiebreak_id: id(0xa0 + v as u8),
};
let groot: Vec<Edition> = [1u64, 2, 3, 4, 6, 7, 8, 9, 10, 11].iter().map(|&v| ed(v)).collect();
assert_eq!(fold(&groot, 0, None).head.map(|i| groot[i].version), Some(4), "strict head stops at the gap");
assert!(fold(&groot, 0, None).gap);
assert_eq!(bootstrap_head(&groot, 0).map(|i| groot[i].version), Some(11), "bootstrap takes the latest across the gap");
let grant: Vec<Edition> = [2u64, 3, 4].iter().map(|&v| ed(v)).collect();
assert!(fold(&grant, 0, None).gap, "no v1 → strict is unanchored");
assert_eq!(bootstrap_head(&grant, 0).map(|i| grant[i].version), Some(4));
assert_eq!(bootstrap_head(&grant, 9), None, "all below floor → no head");
let a = Edition { version: 5, prev_hash: None, self_hash: id(0xAA), created_at: 200, tiebreak_id: id(0xa1) };
let b = Edition { version: 5, prev_hash: None, self_hash: id(0xBB), created_at: 100, tiebreak_id: id(0xb1) };
assert_eq!(bootstrap_head(&[a, b], 0), Some(0), "lower inner id wins at equal version (not created_at)");
}
fn linked(v: u64) -> Edition {
Edition {
version: v,
prev_hash: if v == 1 { None } else { Some(id((v - 1) as u8)) },
self_hash: id(v as u8),
created_at: 100 + v,
tiebreak_id: id(0xc0u8.wrapping_add(v as u8)),
}
}
#[test]
fn union_of_split_relays_folds_contiguously() {
let mut union = vec![linked(1), linked(3), linked(5)];
union.extend(vec![linked(2), linked(4)]);
let r = fold(&union, 0, None);
assert_eq!(r.head.map(|i| union[i].version), Some(5));
assert!(!r.gap, "the union is contiguous v1..v5 even though neither relay had it alone");
}
#[test]
fn fold_is_order_independent_under_scrambled_arrival() {
let scrambled = vec![linked(3), linked(1), linked(5), linked(2), linked(4)];
let r = fold(&scrambled, 0, None);
assert_eq!(r.head.map(|i| scrambled[i].version), Some(5));
assert!(!r.gap);
}
#[test]
fn multiple_gaps_strict_stops_at_first_bootstrap_takes_highest() {
let eds = vec![linked(1), linked(2), linked(4), linked(6)]; let r = fold(&eds, 0, None);
assert_eq!(r.head.map(|i| eds[i].version), Some(2), "strict stops at the first gap");
assert!(r.gap);
assert_eq!(bootstrap_head(&eds, 0).map(|i| eds[i].version), Some(6), "bootstrap takes the highest");
}
#[test]
fn ratchet_advances_when_the_missing_version_arrives() {
let before = vec![linked(1), linked(3)]; let r1 = fold(&before, 0, None);
assert_eq!(r1.head.map(|i| before[i].version), Some(1));
assert!(r1.gap, "v3 can't link without v2");
let after = vec![linked(1), linked(2), linked(3)]; let r2 = fold(&after, 0, None);
assert_eq!(r2.head.map(|i| after[i].version), Some(3));
assert!(!r2.gap, "the gap filled → ratchets to v3");
}
#[test]
fn duplicate_editions_do_not_break_the_fold() {
let eds = vec![linked(1), linked(2), linked(2), linked(3)];
let r = fold(&eds, 0, None);
assert_eq!(r.head.map(|i| eds[i].version), Some(3));
assert!(!r.gap);
}
#[test]
fn forged_middle_edition_does_not_advance_the_head() {
let e1 = linked(1);
let e2_bad = Edition { version: 2, prev_hash: Some(id(0xFF)), self_hash: id(2), created_at: 102, tiebreak_id: id(0xc2) };
let e3 = linked(3); let r = fold(&[e1, e2_bad, e3], 0, None);
assert_eq!(r.head.map(|i| [1u64, 2, 3][i]), Some(1), "head stays at v1 — the v1→v2 link is broken");
assert!(r.gap, "a forged middle edition is a gap, not a silent advance");
}
#[test]
fn floor_plus_one_without_a_floor_hash_is_a_gap() {
let e6 = Edition { version: 6, prev_hash: Some(id(5)), self_hash: id(6), created_at: 600, tiebreak_id: id(0xa6) };
assert!(fold(&[e6], 5, None).gap, "floor+1 with no floor_hash can't be anchored → gap (fail closed)");
}
#[test]
fn all_below_floor_is_no_change_not_a_gap() {
let r = fold(&[linked(1), linked(2)], 5, Some(&id(5)));
assert_eq!(r, FoldResult { head: None, gap: false, anchored: false }, "everything below floor → no candidate, no gap");
}
#[test]
fn fold_version_zero_does_not_panic() {
let e0 = Edition { version: 0, prev_hash: None, self_hash: id(0), created_at: 1, tiebreak_id: id(0xe0) };
assert!(fold(&[e0], 0, None).gap, "a v0 'genesis' is not a valid anchor → gap, no panic");
}
#[test]
fn fold_genesis_with_a_spurious_prev_is_unanchored() {
let e1 = Edition { version: 1, prev_hash: Some(id(0xFF)), self_hash: id(1), created_at: 100, tiebreak_id: id(0xa1) };
assert!(fold(&[e1], 0, None).gap, "v1 with a prev is not a real genesis → gap");
}
#[test]
fn fold_near_u64_max_version_does_not_panic() {
let big = Edition { version: u64::MAX, prev_hash: None, self_hash: id(9), created_at: 1, tiebreak_id: id(0xff) };
assert!(fold(&[big.clone()], 0, None).gap, "u64::MAX is not a genesis → gap, no overflow");
let r = fold(&[big], u64::MAX, Some(&id(9)));
assert!(!r.gap, "re-presenting the held u64::MAX floor is anchored, no overflow in the walk");
}
#[test]
fn fold_a_million_version_gap_holds_at_the_prefix() {
let far = Edition { version: 1_000_000, prev_hash: Some(id(2)), self_hash: id(0x77), created_at: 200, tiebreak_id: id(0xb7) };
let r = fold(&[linked(1), far], 0, None);
assert_eq!(r.head.map(|i| [1u64, 1_000_000][i]), Some(1), "head stays at the genesis prefix");
assert!(r.gap, "a million-version jump is a gap, not a silent advance");
}
#[test]
fn fold_mass_identical_duplicates_collapse() {
let e = linked(1);
let r = fold(&[e.clone(), e.clone(), e.clone(), e], 0, None);
assert_eq!(r.head, Some(0));
assert!(!r.gap, "N identical genesis copies collapse to one, no gap");
}
#[test]
fn fold_a_large_noisy_scrambled_input_does_not_panic() {
let mut eds: Vec<Edition> = (1..=50).map(linked).collect();
eds.extend((1..=50).map(linked)); eds.reverse();
let r = fold(&eds, 0, None);
assert_eq!(r.head.map(|i| eds[i].version), Some(50), "folds to v50 through all the noise");
assert!(!r.gap);
}
#[test]
fn bootstrap_head_on_pathological_inputs() {
assert_eq!(bootstrap_head(&[], 0), None, "empty → None");
assert_eq!(bootstrap_head(&[linked(1), linked(2)], 999), None, "all below floor → None");
let v0 = Edition { version: 0, prev_hash: None, self_hash: id(0), created_at: 1, tiebreak_id: id(0xe0) };
assert!(bootstrap_head(&[v0], 0).is_some(), "a v0 edition still surfaces (≥ floor 0), no panic");
}
#[test]
fn unanchored_head_is_flagged_as_gap() {
let e5 = Edition { version: 5, prev_hash: Some(id(0xFF)), self_hash: id(5), created_at: 500, tiebreak_id: id(0xa5) };
assert!(fold(&[e5], 0, None).gap, "a lone non-genesis edition is unanchored → gap");
let floor_hash = id(0x55);
let e6 = Edition { version: 6, prev_hash: Some(floor_hash), self_hash: id(6), created_at: 600, tiebreak_id: id(0xa6) };
let r = fold(&[e6], 5, Some(&floor_hash));
assert_eq!(r, FoldResult { head: Some(0), gap: false, anchored: true }, "v6 linking to the held v5 hash is anchored");
let e6_bad = Edition { version: 6, prev_hash: Some(id(0xAB)), self_hash: id(6), created_at: 600, tiebreak_id: id(0xa6) };
assert!(fold(&[e6_bad], 5, Some(&floor_hash)).gap, "v6 not linking to the floor is a gap");
}
#[test]
fn tracking_rejects_a_forked_floor_edition() {
let a_hash = id(0xAA);
let a = Edition { version: 5, prev_hash: Some(id(4)), self_hash: a_hash, created_at: 500, tiebreak_id: id(0xa5) };
assert!(!fold(&[a], 5, Some(&a_hash)).gap, "re-presenting our own floor edition is anchored");
let b = Edition { version: 5, prev_hash: Some(id(4)), self_hash: id(0xBB), created_at: 600, tiebreak_id: id(0xb5) };
assert!(fold(&[b], 5, Some(&a_hash)).gap, "a different same-version edition is a fork → rejected, not anchored");
}
#[test]
fn refuses_to_downgrade_below_floor() {
let e1 = Edition { version: 1, prev_hash: None, self_hash: id(1), created_at: 100, tiebreak_id: id(0xa1) };
let e2 = Edition { version: 2, prev_hash: Some(id(1)), self_hash: id(2), created_at: 101, tiebreak_id: id(0xa2) };
let r = fold(&[e1, e2], 2, Some(&id(2)));
assert_eq!(r.head, Some(1));
assert!(!r.gap);
}
#[test]
fn equal_version_fork_resolves_by_lower_inner_id_not_created_at() {
let a = Edition { version: 1, prev_hash: None, self_hash: id(0xAA), created_at: 999, tiebreak_id: id(0x01) };
let b = Edition { version: 1, prev_hash: None, self_hash: id(0xBB), created_at: 0, tiebreak_id: id(0x02) };
assert_eq!(fold(&[a.clone(), b.clone()], 0, None).head, Some(0), "lower id wins even though `a` has the later created_at");
assert_eq!(fold(&[b, a], 0, None).head, Some(1), "and it's independent of arrival order");
}
#[test]
fn detects_a_gap_in_the_chain() {
let e1 = Edition { version: 1, prev_hash: None, self_hash: id(1), created_at: 100, tiebreak_id: id(0xa1) };
let e3 = Edition { version: 3, prev_hash: Some(id(2)), self_hash: id(3), created_at: 102, tiebreak_id: id(0xa3) };
let r = fold(&[e1.clone(), e3], 0, None);
assert_eq!(r.head, Some(0), "head stays at the highest contiguous version (v1)");
assert!(r.gap, "the v2 gap is reported");
let e2_bad = Edition { version: 2, prev_hash: Some(id(0xFF)), self_hash: id(2), created_at: 101, tiebreak_id: id(0xa2) };
let r2 = fold(&[e1.clone(), e2_bad], 0, None);
assert_eq!(r2.head, Some(0));
assert!(r2.gap, "a wrong prev_hash link does not advance the head");
}
}