1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// The one content-hash every SDK shares, so a content-addressed id (a deduped
// memory item, a converged graph node) is identical across languages. Pure and
// dependency-free FNV-1a, no clock or entropy (id GENERATION lives SDK-side. This
// is deterministic hashing, which belongs in the contract). A port reproduces it
// from the same byte segments and the fixture pins the result.
const FNV_OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
const FNV_PRIME: u64 = 0x0000_0100_0000_01b3;
fn fnv1a(salt: u8, segments: &[&[u8]]) -> u64 {
let mut hash = FNV_OFFSET;
hash ^= u64::from(salt);
hash = hash.wrapping_mul(FNV_PRIME);
for segment in segments {
for &byte in *segment {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
}
hash
}
/// A deterministic 128-bit content id over `segments`, the concatenation of which
/// is the content being addressed. Two independently salted FNV-1a passes fill
/// the high and low halves, so the same segments always yield the same id and a
/// different input yields a different one. The caller assembles the segments
/// (e.g. an owner, a discriminator byte, and a body) and the order is part of the
/// contract.
pub fn content_id(segments: &[&[u8]]) -> u128 {
(u128::from(fnv1a(0x4d, segments)) << 64) | u128::from(fnv1a(0xc7, segments))
}
/// The operation id of one step of a materialization setup, derived from the
/// setup's own identity, so a repeated setup repeats every step under the same
/// identity and never applies one twice.
pub fn materialization_operation_id(setup: u128, step: u8) -> u128 {
let id = content_id(&[b"laser.materialization", &setup.to_le_bytes(), &[step]]);
id.max(1)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn given_the_same_segments_when_hashed_then_should_be_stable() {
let a = content_id(&[b"owner", &[1], b"body"]);
let b = content_id(&[b"owner", &[1], b"body"]);
assert_eq!(a, b);
}
#[test]
fn given_different_segments_when_hashed_then_should_differ() {
assert_ne!(content_id(&[b"a"]), content_id(&[b"b"]));
}
#[test]
fn given_a_setup_identity_when_its_steps_are_derived_then_should_match_the_golden_ids() {
let setup = 0x0123_4567_89ab_cdef_0011_2233_4455_6677_u128;
assert_eq!(
materialization_operation_id(setup, 1),
235_494_386_156_137_362_547_501_458_209_079_933_319,
"pin the derived register step"
);
assert_eq!(
materialization_operation_id(setup, 2),
235_494_325_308_908_527_519_489_171_275_872_844_602,
"pin the derived bind step"
);
assert_ne!(
materialization_operation_id(setup, 1),
materialization_operation_id(setup + 1, 1)
);
}
#[test]
fn given_the_pinned_memory_segments_when_hashed_then_should_match_the_golden_id() {
// The cross-SDK hash vector: a zero separator, "agent", a separator, the
// kind byte 1, and body "x". Every port of `content_id` renders this u128
// as the ULID below. Rendered with the crate's Crockford encoder, the
// same one every wire id displays through.
let id = content_id(&[&[0], b"agent", &[0], &[1], b"x"]);
let rendered = crate::agent::RecordId::from_u128(id).to_string();
assert_eq!(rendered, "1A9GVS6SJ6SNS4KY0H19130WCW");
}
}