Skip to main content

Module pointer

Module pointer 

Source
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§

ParsedPointer
Bytes that have been structurally parsed but not yet verified.
Pointer
A paid, mutable, owner-signed reference.
PointerState
The parts of a record that determine its authenticated state.
PointerTarget
A kind tag plus the address it refers to.

Enums§

PointerError
Why a pointer was refused.
PointerTargetKind
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.