Expand description
Pointers — paid mutable references with an immutable owner.
A pointer is a mutable, owner-signed reference stored at an address derived
from its owner’s public key. Unlike a
DataChunk its address is not its
content hash: the address is fixed for the pointer’s life while the bytes
under it change with every update.
The owner key is fixed at creation. There is no lineage, no certificates
and no key rotation: a former owner keeps its key forever, so ownership
cannot be re-keyed by a local rule. An earlier draft of this module
implemented transfer through a genesis object and a chain of transfer
certificates; it was reviewed and withdrawn. See ADR-0016 in ant-node.
That choice is what lets the design be this small: the owner key is inlined in the record, so validating a pointer needs nothing but the pointer — no fetch, no cache, no second close group, nothing that can be missing.
§Finality, and transfer by final redirection
A record at counter == u64::MAX is final: no record replaces it, not
even another one at u64::MAX. That is what makes ownership transferable
without touching the record: the owner signs one last state, at the final
counter, pointing at a pointer the new owner holds the key to
(Pointer::transfer_to). A reader of the old address is redirected to the
new owner’s pointer by every node that holds that final state, the address
never changes, and no node that holds the final state gives it up — a
larger counter does not exist, and an equal one does not replace.
Two different final states can still exist, because only the owner decides
what it signs, and the rule only stops a node from changing its mind. A
node that holds no final state takes whichever one reaches it first. So the
former owner can fork the pointer by signing a second final state and
getting it to nodes the first has not reached: racing both at once, or
sending the second later. Nothing local can settle that. What the rule does
settle is that no node ever gives up a final state it holds, so a fork
cannot take a final state back from the nodes that hold it; it can only
split the nodes that held none. Finding a fork, and reading the state most of the
close group holds, is the client’s job; not taking a rival of a final state
the close group already holds is the node’s. See ADR-0018 in ant-node.
§Encoding
Encoded by hand: canonical, fixed-width, big-endian, no length prefixes and
no serde. The signed payload must not depend on a serializer whose output
can change — 1.0’s pointer called rmp_serde inside its signing routine and
silently signed nothing when it failed.
format_version 1 byte
owner 1952 bytes ML-DSA-65 public key
counter 8 bytes big-endian u64
target 33 bytes 1-byte kind tag + 32-byte address, opaque here
signature 3309 bytes ML-DSA-65 over the preceding 1994 bytes
-----
5303 bytes§Identity
A = BLAKE3::derive_key("autonomi.pointer.address.v1", owner)
state_id = BLAKE3::derive_key("autonomi.pointer.state.v1", body)Derive-key, not a hash of a prefix and the input. A chunk is addressed by a plain hash of its content, so a prefix would put both of these inside the chunk address space for anyone who could write the preimage down: squatting an owner’s address before they used it, or buying a pointer and a chunk with one payment. Both modes still produce 32 bytes, so the ranges overlap; what changed is that crossing them is the preimage problem BLAKE3 is assumed to resist.
A routes and decides which nodes are responsible. state_id names the
authenticated state: it is what sync hints carry, what a fetch decision
compares, and what a quote is paid against. They are separate because
A must be stable for the pointer’s life while the paid identifier must
change with every update, or updates after the first would be free — the
defect this design exists to fix.
§What this module does not do
Payment verification, storage and replication. Those are the node’s job.
Structs§
- Parsed
Pointer - Bytes that have been structurally parsed but not yet verified.
- Pointer
- A paid, mutable, owner-signed reference.
- Pointer
State - The parts of a record that determine its authenticated state.
- Pointer
Target - A kind tag plus the address it refers to.
Enums§
- Pointer
Error - Why a pointer was refused.
- Pointer
Target Kind - What a pointer points at, as clients interpret it.
Constants§
- DATA_
TYPE_ POINTER - Data type identifier for pointers, alongside
DATA_TYPE_CHUNK. - FINAL_
COUNTER - The final counter. A record signed at it is never replaced.
- ML_
DSA_ 65_ PUBLIC_ KEY_ LEN - Length of a raw ML-DSA-65 public key.
- ML_
DSA_ 65_ SIGNATURE_ LEN - Length of a raw ML-DSA-65 signature.
- POINTER_
BODY_ LEN - Length of the signed body: everything before the signature.
- POINTER_
FORMAT_ VERSION - Wire format discriminator carried by every pointer.
- POINTER_
WIRE_ LEN - Total size of an encoded pointer.
- TARGET_
WIRE_ LEN - Length of an encoded
PointerTarget: a kind tag plus an address.
Functions§
- pointer_
address - Derive a pointer address from an owner key.
- state_
id_ for_ body - Derive the authenticated-state identifier from a signed body.