use saorsa_pqc::api::sig::{
ml_dsa_65, MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature, MlDsaVariant,
};
use crate::chunk::XORNAME_LEN;
use crate::data_types::XorName;
pub const DATA_TYPE_POINTER: u32 = 1;
pub const POINTER_FORMAT_VERSION: u8 = 1;
pub const FINAL_COUNTER: u64 = u64::MAX;
pub const ML_DSA_65_PUBLIC_KEY_LEN: usize = 1952;
pub const ML_DSA_65_SIGNATURE_LEN: usize = 3309;
pub const TARGET_WIRE_LEN: usize = 1 + XORNAME_LEN;
const OWNER_OFFSET: usize = 1;
const COUNTER_OFFSET: usize = OWNER_OFFSET + ML_DSA_65_PUBLIC_KEY_LEN;
const TARGET_OFFSET: usize = COUNTER_OFFSET + 8;
pub const POINTER_BODY_LEN: usize = TARGET_OFFSET + TARGET_WIRE_LEN;
pub const POINTER_WIRE_LEN: usize = POINTER_BODY_LEN + ML_DSA_65_SIGNATURE_LEN;
const CONTEXT_ADDRESS: &str = "autonomi.pointer.address.v1";
const CONTEXT_STATE: &str = "autonomi.pointer.state.v1";
const SIGNING_CONTEXT: &[u8] = b"autonomi.pointer.head.v1";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PointerError {
WrongLength(usize),
UnknownFormatVersion(u8),
InvalidOwnerKey(String),
InvalidSignatureFormat(String),
SignatureInvalid,
SigningFailed(String),
CounterExhausted,
}
impl std::fmt::Display for PointerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::WrongLength(len) => {
write!(f, "pointer must be {POINTER_WIRE_LEN} bytes, got {len}")
}
Self::UnknownFormatVersion(version) => {
write!(f, "unknown pointer format version {version}")
}
Self::InvalidOwnerKey(reason) => write!(f, "invalid pointer owner key: {reason}"),
Self::InvalidSignatureFormat(reason) => {
write!(f, "invalid pointer signature format: {reason}")
}
Self::SignatureInvalid => {
write!(f, "pointer signature does not verify under its owner key")
}
Self::SigningFailed(reason) => write!(f, "pointer signing failed: {reason}"),
Self::CounterExhausted => write!(
f,
"pointer is final: its counter is at u64::MAX, and no later state \
can replace it"
),
}
}
}
impl std::error::Error for PointerError {}
#[must_use]
pub fn pointer_address(owner: &MlDsaPublicKey) -> XorName {
blake3::derive_key(CONTEXT_ADDRESS, &owner.to_bytes())
}
#[must_use]
pub fn state_id_for_body(body: &[u8]) -> XorName {
blake3::derive_key(CONTEXT_STATE, body)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PointerTargetKind {
Chunk,
Pointer,
}
impl PointerTargetKind {
#[must_use]
pub const fn tag(self) -> u8 {
match self {
Self::Chunk => 0,
Self::Pointer => 1,
}
}
#[must_use]
pub const fn from_tag(tag: u8) -> Option<Self> {
match tag {
0 => Some(Self::Chunk),
1 => Some(Self::Pointer),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct PointerTarget {
kind_tag: u8,
pub address: XorName,
}
impl PointerTarget {
#[must_use]
pub const fn new(kind: PointerTargetKind, address: XorName) -> Self {
Self {
kind_tag: kind.tag(),
address,
}
}
#[must_use]
pub const fn from_raw_tag(kind_tag: u8, address: XorName) -> Self {
Self { kind_tag, address }
}
#[must_use]
pub const fn kind_tag(&self) -> u8 {
self.kind_tag
}
#[must_use]
pub const fn kind(&self) -> Option<PointerTargetKind> {
PointerTargetKind::from_tag(self.kind_tag)
}
#[must_use]
pub fn to_bytes(&self) -> [u8; TARGET_WIRE_LEN] {
let mut out = [0u8; TARGET_WIRE_LEN];
if let Some((tag, address)) = out.split_first_mut() {
*tag = self.kind_tag;
address.copy_from_slice(&self.address);
}
out
}
fn from_bytes(bytes: &[u8]) -> Option<Self> {
let (tag, rest) = bytes.split_first()?;
if rest.len() != XORNAME_LEN {
return None;
}
let mut address = [0u8; XORNAME_LEN];
address.copy_from_slice(rest);
Some(Self {
kind_tag: *tag,
address,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct MergeRank {
counter: u64,
target: std::cmp::Reverse<[u8; TARGET_WIRE_LEN]>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PointerState {
pub state_id: XorName,
pub address: XorName,
pub counter: u64,
pub target: PointerTarget,
}
impl PointerState {
pub fn parse(bytes: &[u8]) -> Result<Self, PointerError> {
Self::parse_with_owner(bytes).map(|(state, _)| state)
}
fn parse_with_owner(bytes: &[u8]) -> Result<(Self, MlDsaPublicKey), PointerError> {
if bytes.len() != POINTER_WIRE_LEN {
return Err(PointerError::WrongLength(bytes.len()));
}
let version = bytes
.first()
.copied()
.ok_or(PointerError::WrongLength(bytes.len()))?;
if version != POINTER_FORMAT_VERSION {
return Err(PointerError::UnknownFormatVersion(version));
}
let owner_bytes = bytes
.get(OWNER_OFFSET..COUNTER_OFFSET)
.ok_or(PointerError::WrongLength(bytes.len()))?;
let owner = MlDsaPublicKey::from_bytes(MlDsaVariant::MlDsa65, owner_bytes)
.map_err(|e| PointerError::InvalidOwnerKey(e.to_string()))?;
let counter_bytes: [u8; 8] = bytes
.get(COUNTER_OFFSET..TARGET_OFFSET)
.and_then(|slice| slice.try_into().ok())
.ok_or(PointerError::WrongLength(bytes.len()))?;
let target = bytes
.get(TARGET_OFFSET..POINTER_BODY_LEN)
.and_then(PointerTarget::from_bytes)
.ok_or(PointerError::WrongLength(bytes.len()))?;
let body = bytes
.get(..POINTER_BODY_LEN)
.ok_or(PointerError::WrongLength(bytes.len()))?;
Ok((
Self {
state_id: state_id_for_body(body),
address: pointer_address(&owner),
counter: u64::from_be_bytes(counter_bytes),
target,
},
owner,
))
}
#[must_use]
fn rank(&self) -> MergeRank {
MergeRank {
counter: self.counter,
target: std::cmp::Reverse(self.target.to_bytes()),
}
}
#[must_use]
pub const fn is_terminal(&self) -> bool {
self.counter == FINAL_COUNTER
}
#[must_use]
pub const fn transferred_to(&self) -> Option<XorName> {
transfer_target(self.counter, self.target)
}
#[must_use]
pub fn replaces(&self, other: &Self) -> bool {
self.address == other.address && !other.is_terminal() && self.rank() > other.rank()
}
}
const fn transfer_target(counter: u64, target: PointerTarget) -> Option<XorName> {
match target.kind() {
Some(PointerTargetKind::Pointer) if counter == FINAL_COUNTER => Some(target.address),
_ => None,
}
}
pub struct ParsedPointer {
bytes: Vec<u8>,
state: PointerState,
owner: MlDsaPublicKey,
}
impl std::fmt::Debug for ParsedPointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParsedPointer")
.field("address", &hex::encode(self.state.address))
.field("counter", &self.state.counter)
.finish_non_exhaustive()
}
}
impl ParsedPointer {
pub fn parse(bytes: Vec<u8>) -> Result<Self, PointerError> {
let (state, owner) = PointerState::parse_with_owner(&bytes)?;
Ok(Self {
bytes,
state,
owner,
})
}
#[must_use]
pub const fn state(&self) -> &PointerState {
&self.state
}
}
#[derive(Clone)]
pub struct Pointer {
version: u8,
owner: MlDsaPublicKey,
counter: u64,
target: PointerTarget,
sig: MlDsaSignature,
}
impl std::fmt::Debug for Pointer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Pointer")
.field("address", &hex::encode(self.address()))
.field("counter", &self.counter)
.field("target_tag", &self.target.kind_tag())
.field("target", &hex::encode(self.target.address))
.field("state_id", &hex::encode(self.state_id()))
.finish_non_exhaustive()
}
}
impl Pointer {
pub fn sign(
secret_key: &MlDsaSecretKey,
owner: &MlDsaPublicKey,
counter: u64,
target: PointerTarget,
) -> Result<Self, PointerError> {
let body = encode_body(POINTER_FORMAT_VERSION, owner, counter, target)?;
let signature = ml_dsa_65()
.sign_with_context(secret_key, &body, SIGNING_CONTEXT)
.map_err(|e| PointerError::SigningFailed(e.to_string()))?;
let mut bytes = body;
bytes.extend_from_slice(&signature.to_bytes());
Self::from_bytes(&bytes)
}
pub fn create(
secret_key: &MlDsaSecretKey,
owner: &MlDsaPublicKey,
target: PointerTarget,
) -> Result<Self, PointerError> {
Self::sign(secret_key, owner, 0, target)
}
pub fn update(
&self,
secret_key: &MlDsaSecretKey,
target: PointerTarget,
) -> Result<Self, PointerError> {
Self::sign(secret_key, &self.owner, self.next_counter()?, target)
}
pub fn finalize(
&self,
secret_key: &MlDsaSecretKey,
target: PointerTarget,
) -> Result<Self, PointerError> {
if self.is_terminal() {
return Err(PointerError::CounterExhausted);
}
Self::sign(secret_key, &self.owner, FINAL_COUNTER, target)
}
pub fn transfer_to(
&self,
secret_key: &MlDsaSecretKey,
recipient: XorName,
) -> Result<Self, PointerError> {
self.finalize(
secret_key,
PointerTarget::new(PointerTargetKind::Pointer, recipient),
)
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PointerError> {
Self::verify_parsed(ParsedPointer::parse(bytes.to_vec())?)
}
pub fn verify_parsed(parsed: ParsedPointer) -> Result<Self, PointerError> {
let ParsedPointer {
bytes,
state,
owner,
} = parsed;
let body = bytes
.get(..POINTER_BODY_LEN)
.ok_or(PointerError::WrongLength(bytes.len()))?;
let signature_bytes = bytes
.get(POINTER_BODY_LEN..)
.ok_or(PointerError::WrongLength(bytes.len()))?;
let signature = MlDsaSignature::from_bytes(MlDsaVariant::MlDsa65, signature_bytes)
.map_err(|e| PointerError::InvalidSignatureFormat(e.to_string()))?;
let valid = ml_dsa_65()
.verify_with_context(&owner, body, &signature, SIGNING_CONTEXT)
.map_err(|e| PointerError::InvalidSignatureFormat(e.to_string()))?;
if !valid {
return Err(PointerError::SignatureInvalid);
}
Ok(Self {
version: POINTER_FORMAT_VERSION,
owner,
counter: state.counter,
target: state.target,
sig: signature,
})
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
let mut out = self.body();
out.extend_from_slice(&self.sig.to_bytes());
out
}
fn body(&self) -> Vec<u8> {
encode_body(self.version, &self.owner, self.counter, self.target)
.unwrap_or_else(|_| Vec::with_capacity(POINTER_BODY_LEN))
}
#[must_use]
pub const fn owner(&self) -> &MlDsaPublicKey {
&self.owner
}
#[must_use]
pub fn state(&self) -> PointerState {
PointerState {
state_id: self.state_id(),
address: self.address(),
counter: self.counter,
target: self.target,
}
}
#[must_use]
pub const fn counter(&self) -> u64 {
self.counter
}
#[must_use]
pub const fn target(&self) -> PointerTarget {
self.target
}
#[must_use]
pub fn address(&self) -> XorName {
pointer_address(&self.owner)
}
#[must_use]
pub fn state_id(&self) -> XorName {
state_id_for_body(&self.body())
}
pub fn next_counter(&self) -> Result<u64, PointerError> {
self.counter
.checked_add(1)
.ok_or(PointerError::CounterExhausted)
}
#[must_use]
pub const fn is_terminal(&self) -> bool {
self.counter == FINAL_COUNTER
}
#[must_use]
pub const fn transferred_to(&self) -> Option<XorName> {
transfer_target(self.counter, self.target)
}
#[must_use]
pub fn replaces(&self, other: &Self) -> bool {
self.state().replaces(&other.state())
}
}
fn encode_body(
version: u8,
owner: &MlDsaPublicKey,
counter: u64,
target: PointerTarget,
) -> Result<Vec<u8>, PointerError> {
let owner_bytes = owner.to_bytes();
if owner_bytes.len() != ML_DSA_65_PUBLIC_KEY_LEN {
return Err(PointerError::InvalidOwnerKey(format!(
"owner key must be {ML_DSA_65_PUBLIC_KEY_LEN} bytes, got {}",
owner_bytes.len()
)));
}
let mut body = Vec::with_capacity(POINTER_BODY_LEN);
body.push(version);
body.extend_from_slice(&owner_bytes);
body.extend_from_slice(&counter.to_be_bytes());
body.extend_from_slice(&target.to_bytes());
Ok(body)
}
#[cfg(test)]
#[allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
reason = "the crate guide allows these in tests"
)]
mod tests {
use super::*;
fn keypair(seed: u8) -> (MlDsaPublicKey, MlDsaSecretKey) {
ml_dsa_65().generate_keypair_from_seed(&[seed; 32])
}
fn signed(seed: u8, counter: u64, target_byte: u8) -> Pointer {
let (pk, sk) = keypair(seed);
let target = PointerTarget::new(PointerTargetKind::Chunk, [target_byte; XORNAME_LEN]);
Pointer::sign(&sk, &pk, counter, target).expect("sign")
}
#[test]
fn sizes_match_the_design() {
assert_eq!(POINTER_BODY_LEN, 1994);
assert_eq!(POINTER_WIRE_LEN, 5303);
assert_eq!(signed(1, 0, 0).to_bytes().len(), POINTER_WIRE_LEN);
}
#[test]
fn roundtrips_and_validates() {
let record = signed(1, 42, 7);
let parsed = Pointer::from_bytes(&record.to_bytes()).expect("reparse");
assert_eq!(parsed.counter(), 42);
assert_eq!(parsed.address(), record.address());
assert_eq!(parsed.state_id(), record.state_id());
}
#[test]
fn a_tampered_record_does_not_verify() {
let record = signed(1, 5, 5);
let mut bytes = record.to_bytes();
if let Some(byte) = bytes.get_mut(COUNTER_OFFSET + 7) {
*byte ^= 1;
}
assert!(matches!(
Pointer::from_bytes(&bytes),
Err(PointerError::SignatureInvalid)
));
}
#[test]
fn an_unknown_format_version_is_refused() {
let record = signed(1, 1, 1);
let mut bytes = record.to_bytes();
if let Some(byte) = bytes.first_mut() {
*byte = 2;
}
assert!(matches!(
Pointer::from_bytes(&bytes),
Err(PointerError::UnknownFormatVersion(2))
));
}
#[test]
fn the_format_version_reaches_the_paid_identifier() {
let record = signed(1, 3, 3);
let mut body = record
.to_bytes()
.get(..POINTER_BODY_LEN)
.expect("body")
.to_vec();
let baseline = state_id_for_body(&body);
assert_eq!(baseline, record.state_id());
for version in 0u8..=255 {
if version == POINTER_FORMAT_VERSION {
continue;
}
if let Some(byte) = body.first_mut() {
*byte = version;
}
assert_ne!(
state_id_for_body(&body),
baseline,
"version {version} must not share version 1's paid identifier"
);
}
}
#[test]
fn different_owners_are_not_comparable() {
let mine = signed(1, 1, 9);
let theirs = signed(2, 500, 0);
assert!(!mine.replaces(&theirs));
assert!(!theirs.replaces(&mine));
}
#[test]
fn the_counter_is_terminal_at_its_maximum() {
let ordinary = signed(1, 0, 0);
assert!(!ordinary.is_terminal());
assert_eq!(ordinary.next_counter().expect("next"), 1);
let terminal = signed(1, u64::MAX, 0);
assert!(terminal.is_terminal());
assert_eq!(terminal.next_counter(), Err(PointerError::CounterExhausted));
assert!(terminal.next_counter().is_err());
}
#[test]
fn nothing_out_ranks_a_terminal_counter() {
let terminal = signed(1, u64::MAX, 5);
for counter in [0u64, 1, 42, u64::MAX - 1] {
let earlier = signed(1, counter, 0);
assert!(
!earlier.replaces(&terminal),
"counter {counter} must not replace a terminal pointer"
);
assert!(terminal.replaces(&earlier));
}
}
#[test]
fn a_final_state_is_replaced_by_nothing_not_even_a_smaller_target() {
let first = signed(1, FINAL_COUNTER, 9);
let smaller_target = signed(1, FINAL_COUNTER, 1);
assert!(
!smaller_target.replaces(&first),
"a smaller target no longer wins"
);
assert!(!first.replaces(&smaller_target));
let low = signed(1, 7, 1);
let high = signed(1, 7, 9);
assert!(
low.replaces(&high),
"smaller target still wins below the end"
);
}
#[test]
fn a_state_at_the_final_counter_is_final_whatever_it_targets() {
let (pk, sk) = keypair(40);
let chunk = PointerTarget::new(PointerTargetKind::Chunk, [4; XORNAME_LEN]);
let pointer = PointerTarget::new(PointerTargetKind::Pointer, [5; XORNAME_LEN]);
let unknown = PointerTarget::from_raw_tag(200, [6; XORNAME_LEN]);
for target in [chunk, pointer, unknown] {
let held = Pointer::sign(&sk, &pk, FINAL_COUNTER, target).expect("sign");
assert!(held.is_terminal());
assert!(held.state().is_terminal());
for other in [chunk, pointer, unknown] {
for counter in [0, 1, FINAL_COUNTER - 1, FINAL_COUNTER] {
let arrival = Pointer::sign(&sk, &pk, counter, other).expect("sign");
assert!(
!arrival.replaces(&held),
"counter {counter} must not replace a final state"
);
}
}
}
}
#[test]
fn a_transfer_is_a_final_state_that_targets_the_recipient_pointer() {
let (pk, sk) = keypair(41);
let recipient = [0x42; XORNAME_LEN];
let current = Pointer::create(
&sk,
&pk,
PointerTarget::new(PointerTargetKind::Chunk, [1; XORNAME_LEN]),
)
.expect("create");
assert_eq!(current.transferred_to(), None);
let transfer = current.transfer_to(&sk, recipient).expect("transfer");
assert_eq!(transfer.counter(), FINAL_COUNTER);
assert_eq!(transfer.address(), current.address(), "the address stays");
assert_eq!(
transfer.target(),
PointerTarget::new(PointerTargetKind::Pointer, recipient)
);
assert_eq!(transfer.transferred_to(), Some(recipient));
assert_eq!(transfer.state().transferred_to(), Some(recipient));
assert!(transfer.replaces(¤t));
assert!(matches!(
transfer.update(&sk, current.target()),
Err(PointerError::CounterExhausted)
));
assert!(
matches!(
transfer.transfer_to(&sk, [0; XORNAME_LEN]),
Err(PointerError::CounterExhausted)
),
"finalizing twice is refused rather than signed into a fork"
);
let grinded = Pointer::sign(
&sk,
&pk,
FINAL_COUNTER,
PointerTarget::new(PointerTargetKind::Pointer, [0; XORNAME_LEN]),
)
.expect("sign");
assert!(!grinded.replaces(&transfer));
}
#[test]
fn an_earlier_record_can_be_finalized_twice_and_neither_replaces_the_other() {
let (pk, sk) = keypair(44);
let current = Pointer::create(
&sk,
&pk,
PointerTarget::new(PointerTargetKind::Chunk, [1; XORNAME_LEN]),
)
.expect("create");
let to_a = current.transfer_to(&sk, [0xA; XORNAME_LEN]).expect("first");
let to_b = current
.transfer_to(&sk, [0xB; XORNAME_LEN])
.expect("second");
assert_ne!(to_a.state().state_id, to_b.state().state_id);
assert!(to_a.replaces(¤t) && to_b.replaces(¤t));
assert!(
!to_a.replaces(&to_b),
"neither final state replaces the other"
);
assert!(!to_b.replaces(&to_a));
}
#[test]
fn only_a_final_pointer_target_is_a_transfer() {
let (pk, sk) = keypair(43);
let forward = PointerTarget::new(PointerTargetKind::Pointer, [7; XORNAME_LEN]);
let chunk = PointerTarget::new(PointerTargetKind::Chunk, [7; XORNAME_LEN]);
let forwarding = Pointer::sign(&sk, &pk, FINAL_COUNTER - 1, forward).expect("sign");
assert_eq!(forwarding.transferred_to(), None);
let frozen = Pointer::sign(&sk, &pk, FINAL_COUNTER, chunk).expect("sign");
assert_eq!(frozen.transferred_to(), None);
let handed_on = Pointer::sign(&sk, &pk, FINAL_COUNTER, forward).expect("sign");
assert_eq!(handed_on.transferred_to(), Some([7; XORNAME_LEN]));
}
#[test]
fn with_final_states_a_fold_keeps_the_first_final_it_meets() {
let fold = |arrivals: &[&Pointer]| -> XorName {
let mut held: Option<&Pointer> = None;
for arrival in arrivals {
if held.is_none_or(|h| arrival.replaces(h)) {
held = Some(arrival);
}
}
held.expect("something arrived").state_id()
};
let early = signed(44, 3, 1);
let final_a = signed(44, FINAL_COUNTER, 9);
let final_b = signed(44, FINAL_COUNTER, 1);
assert_eq!(fold(&[&early, &final_a, &final_b]), final_a.state_id());
assert_eq!(fold(&[&final_b, &early, &final_a]), final_b.state_id());
assert_eq!(fold(&[&final_a, &final_b, &early]), final_a.state_id());
assert_eq!(fold(&[&early, &final_b]), final_b.state_id());
}
#[test]
fn create_starts_at_zero_and_update_adds_one() {
let (pk, sk) = keypair(20);
let target = |b: u8| PointerTarget::new(PointerTargetKind::Chunk, [b; XORNAME_LEN]);
let created = Pointer::create(&sk, &pk, target(1)).expect("create");
assert_eq!(created.counter(), 0);
let first = created.update(&sk, target(2)).expect("update");
assert_eq!(first.counter(), 1);
assert!(first.replaces(&created));
let second = first.update(&sk, target(3)).expect("update");
assert_eq!(second.counter(), 2);
assert!(second.replaces(&first));
}
#[test]
fn any_larger_counter_replaces_so_counters_may_skip() {
let held = signed(21, 5, 7);
for later in [6u64, 7, 99, u64::MAX] {
assert!(
signed(21, later, 7).replaces(&held),
"counter {later} must replace counter 5"
);
}
for earlier in [0u64, 4] {
assert!(
!signed(21, earlier, 7).replaces(&held),
"counter {earlier} must not replace counter 5"
);
}
}
#[test]
fn a_fork_at_one_counter_resolves_by_target_and_any_later_counter_heals_it() {
let smaller = signed(25, 5, 1);
let larger = signed(25, 5, 9);
assert!(smaller.replaces(&larger), "smaller target bytes win");
assert!(!larger.replaces(&smaller));
for later in [6u64, 50] {
let healed = signed(25, later, 9);
assert!(healed.replaces(&smaller));
assert!(healed.replaces(&larger));
}
}
#[test]
fn a_terminal_pointer_cannot_be_updated() {
let (pk, sk) = keypair(24);
let target = PointerTarget::new(PointerTargetKind::Chunk, [1; XORNAME_LEN]);
let terminal = Pointer::sign(&sk, &pk, u64::MAX, target).expect("sign");
assert!(
terminal.update(&sk, target).is_err(),
"the counter must not wrap around into a fresh-looking pointer"
);
}
#[test]
fn re_encoding_is_byte_identical() {
let (pk, sk) = keypair(30);
for counter in [0u64, 1, 42, u64::MAX] {
for tag in [0u8, 1, 200] {
let target = PointerTarget::from_raw_tag(tag, [tag; XORNAME_LEN]);
let signed = Pointer::sign(&sk, &pk, counter, target).expect("sign");
let wire = signed.to_bytes();
let parsed = Pointer::from_bytes(&wire).expect("parse");
assert_eq!(parsed.to_bytes(), wire, "counter {counter}, tag {tag}");
assert!(Pointer::from_bytes(&parsed.to_bytes()).is_ok());
}
}
}
#[test]
fn equal_state_never_replaces() {
let variants: Vec<Pointer> = (0..8).map(|_| signed(1, 7, 3)).collect();
for a in &variants {
for b in &variants {
assert!(!a.replaces(b));
assert_eq!(a.state_id(), b.state_id());
}
}
}
#[test]
fn the_obvious_chunk_preimages_miss_both_pointer_identities() {
let (pk, sk) = keypair(77);
let record = Pointer::create(
&sk,
&pk,
PointerTarget::new(PointerTargetKind::Chunk, [3; XORNAME_LEN]),
)
.expect("create");
let address_preimage = [b"autonomi.pointer.address.v1".as_slice(), &pk.to_bytes()].concat();
assert_ne!(
*blake3::hash(&address_preimage).as_bytes(),
record.address(),
"a chunk holding this content must not land on the pointer's address"
);
let body = record.to_bytes();
let body = body.get(..POINTER_BODY_LEN).expect("body");
let state_preimage = [b"autonomi.pointer.state.v1".as_slice(), body].concat();
assert_ne!(
*blake3::hash(&state_preimage).as_bytes(),
record.state_id(),
"a chunk holding this content must not land on the paid identifier"
);
for content in [
pk.to_bytes().as_slice(),
body,
b"autonomi.pointer.address.v1",
] {
let address = *blake3::hash(content).as_bytes();
assert_ne!(address, record.address());
assert_ne!(address, record.state_id());
}
}
#[test]
fn the_rule_is_a_strict_order_so_only_final_states_depend_on_arrival_order() {
let records = [
signed(1, 0, 9),
signed(1, 2, 1),
signed(1, 5, 1),
signed(1, 5, 2),
signed(1, FINAL_COUNTER, 3),
signed(1, FINAL_COUNTER, 4),
];
for a in &records {
assert!(!a.replaces(a), "an equal state never replaces");
for b in &records {
assert!(
!(a.replaces(b) && b.replaces(a)),
"two records cannot each replace the other"
);
let both_final = a.is_terminal() && b.is_terminal();
assert!(
a.replaces(b) || b.replaces(a) || a.state_id() == b.state_id() || both_final,
"distinct states are always ordered unless both are final"
);
for c in &records {
assert!(
!(a.replaces(b) && b.replaces(c)) || a.replaces(c),
"the order is transitive"
);
}
}
}
}
}