Addressed Types

Problem

In addressed systems, data is identified by its hash. All addressing reduces to hashing — even physical locations can be geohashed. Producing the hash requires canonical serialization — the same data must always serialize to the same bytes. Rust has no built-in concept of canonical serialization or hash-derived identity.

Solution

The Addressed derive macro generates canonical serialization and a .particle() method.

Syntax

#[derive(Addressed)]
struct Cyberlink {
    from: Particle,
    to: Particle,
    agent: Address,
    height: u64,
}

let link = Cyberlink { from: a, to: b, agent: alice, height: 100 };
let id: Particle = link.particle();  // Deterministic, canonical hash

// Two identical structs always produce the same Particle
let link2 = Cyberlink { from: a, to: b, agent: alice, height: 100 };
assert_eq!(link.particle(), link2.particle());

Canonical Serialization Rules

The derived serializer follows strict rules:

  1. Fields are serialized in declaration order (not alphabetical, not random)
  2. Integers are serialized as little-endian fixed-width bytes
  3. bool is serialized as a single u8 (0 = false, 1 = true)
  4. Option<T> is serialized as a u8 tag (0 = None, 1 = Some) followed by T data if Some
  5. Fixed-length arrays ([T; N]) are serialized as N elements with no length prefix (length is known from the type)
  6. Variable-length types (slices, BoundedVec) are prefixed with a u32 length
  7. No padding bytes between fields
  8. Enums serialized as discriminant (u32) + variant data
  9. Nested Addressed types are serialized as their Particle (64 bytes), not expanded

Hash function: Hemera — a Poseidon2 sponge over the Goldilocks field (p = 2⁶⁴ − 2³² + 1), 64-byte output, producing a Particle. Parameters: state width 16 elements, rate 8, capacity 8, 8 full rounds, 64 partial rounds, S-box x⁷. Capacity 8 (256 bits) provides long-term collision resistance matching the permanence of particle addresses. See the Hemera spec for the full parameter rationale.

Compile-Time Checks

Check Error
Field type not serializable error[RS301]: type MyOpaqueType does not implement CanonicalSerialize
Contains f32/f64 error[RS302]: floating point types are not canonically serializable
Contains raw pointers error[RS303]: pointers cannot be addressed
Contains HashMap error[RS304]: HashMap has non-deterministic serialization; use BTreeMap
Contains usize or isize error[RS305]: usize/isize width is platform-dependent; use u32 or u64
Enum has #[repr] wider than u32 error[RS306]: Addressed enum discriminant must fit in u32; #[repr(u64)] is not supported

Serialization Order for Collections

BoundedMap<K, V, N> serializes entries in key-sorted order (ascending). This matches BTreeMap iteration order and guarantees canonical output regardless of insertion order.

Implementation

Implemented as a proc-macro (no compiler changes required). Works in both standard Rust and Rs editions. ~500 lines.

Error Reference

See errors/addressed.md for detailed descriptions of RS301–RS306.

Dimensions

rs/reference/errors/addressed
Addressed Errors (RS301–RS306) [Back to Error Catalog](/rs-reference-errors) | Spec: [addressed.md](/rs-reference-addressed) Enforcement: proc-macro (`#[derive(Addressed)]`) + rsc lint (transitivity verification). RS301: Type not serializable Every field in an `Addressed` struct must implement…
rs/macros/src/addressed
addressed

Local Graph