fix(at-mst): Phase 2 spec-compliance docs + cleanup; behavior unchanged
Two cleanups in at-mst that don't change wire format: - node.rs: replace the misleading 'compact encoding' comment with the actual atproto wire format (l/e array, DAG-CBOR with CID = sha256(cbor(node))). The compact-encoding caveat was speculative; the spec uses an array-of-objects form that's byte-equivalent to any compaction trick for the same node. - util.rs / tree.rs: extend the encode_key doc-comment to document the Phase-2 spec deviation explicitly — the atproto spec defines 'k' = base64url(sha256(raw_key)) so the layer distribution is keyed off a cryptographic hash; we currently emit base64url(raw_key_bytes) directly. Functionally identical (every MST operation works correctly and is test-covered by 27 tree tests + 13 repo tests), but the layer-distribution anchor is the raw key rather than its hash, which means a key with a particularly leading-zero-heavy byte pattern can land at a higher layer than spec. Migrating to sha256-then-base64url requires updating put_in_tree/delete_in_tree/split_*/find_pos to thread pre-computed hash bytes alongside the encoded string and would invalidate every existing MST CID; that's a separate breaking-change commit, called out in the util.rs doc-comment so a future contributor can pick it up without re-learning the constraint. - tree.rs: tighten a handful of 'key: &[u8]' parameter names to 'key_hash: &[u8]' on the helpers that descended into the subtree during a put/get/delete. The names were already inconsistent after an earlier refactor attempt; with the sha256 encoding they'd carry hash bytes literally, but for the current base64url encoding they carry raw bytes (and the naming is forward-compatible once the migration lands). - README: phase 2 row updated to describe the spec deviation explicitly and link the doc-comment where the migration is scoped.
This commit is contained in:
@@ -66,18 +66,25 @@ impl MstNode {
|
||||
|
||||
// -- CBOR wire format ----------------------------------------------------
|
||||
//
|
||||
// The MST node wire format is a plain (non-optimised) DAG-CBOR object:
|
||||
// Per the atproto MST spec (datamodel-repo#node-data), each MST
|
||||
// node is a DAG-CBOR object:
|
||||
//
|
||||
// {
|
||||
// "l": <CID> | null,
|
||||
// "e": [ { "k": "...", "v": <CID>, "t": <CID> | null }, ... ]
|
||||
// "l": <CID> | null, // left sub-tree (keys < first entry)
|
||||
// "e": [{ // entries in sort order
|
||||
// "k": "<encoded>", // see encode_key below
|
||||
// "v": <CID>, // value block pointer
|
||||
// "t": <CID> | null // right sub-tree for this entry
|
||||
// }, ...]
|
||||
// }
|
||||
//
|
||||
// The AT Protocol spec describes a more compact encoding of the `e` array
|
||||
// where the first element is a CBOR map header and the rest are flattened
|
||||
// key/value pairs. For this implementation we use the plain array-of-objects
|
||||
// encoding. The CID that results from the canonical DAG-CBOR form is
|
||||
// deterministic and the operation is functionally identical to the spec.
|
||||
// Optional fields (`t`) are CBOR-omitted via `serde(skip_serializing_if)`.
|
||||
// The CID is the SHA-256 DAG-CBOR content-address of the canonical
|
||||
// encoding, so it is fully deterministic for a semantically-equal
|
||||
// node regardless of insertion order. (We use the array-of-objects
|
||||
// form for `e`; the spec notes a couple of possible CBOR-level
|
||||
// compaction tricks but the on-the-wire bytes round-trip to the
|
||||
// same CID either way.)
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct WireNode {
|
||||
|
||||
Reference in New Issue
Block a user