2.9 KiB
advertise
Purpose
Encodes and decodes cd:advertise envelope payloads — the gossip messages a peer emits to announce which chunks it currently holds for a given file. Enforces DMP-08 (presence bitset's bitLength MUST equal totalChunks; padding bits MUST be zero), DMP-09 (generation field is unsigned 32-bit), and DMP-21 (presence MUST be framed via @suaveplan/bitpacker-compressed).
Features
encodeAdvertisePayload(payload)— Zod-validates, cross-checks the presence bitset's embeddedbitLengthagainsttotalChunks, base64-wraps the bitset, and emits UTF-8 JSON bytes.decodeAdvertisePayload(bytes)— inverse, with the same DMP-08 cross-check on the receive side so receivers never trust a malformed advertisement.- Both functions throw
DmpErrorwithDMP.BITSET_LENGTH_MISMATCH,DMP.BITSET_PADDING_NONZERO,DMP.CODEC_MISMATCH, orDMP.ENVELOPE_MALFORMEDdepending on the failure class.
Usage: Basic Example
import {
chunkIdsToRawBitset,
decodeAdvertisePayload,
encodeAdvertisePayload,
encodeFramedBitset,
} from "@suaveplan/drive-mesh-protocol";
const totalChunks = 64;
const raw = chunkIdsToRawBitset([0, 5, 17, 63], totalChunks);
const presence = encodeFramedBitset(raw, totalChunks);
const wire = encodeAdvertisePayload({
fileId: "f".repeat(64),
presence,
totalChunks,
generation: 7,
timestamp: Date.now(),
});
const back = decodeAdvertisePayload(wire);
Usage: Advanced Example
import {
chunkIdsToRawBitset,
decodeAdvertisePayload,
encodeAdvertisePayload,
encodeFramedBitset,
GenerationTracker,
} from "@suaveplan/drive-mesh-protocol";
const tracker = new GenerationTracker();
function handleIncomingAdvertise(bytes: Uint8Array, peerId: string): void {
const adv = decodeAdvertisePayload(bytes);
const r = tracker.observe(peerId, adv.fileId, adv.generation);
if (!r.accept) return; // DMP-09 stale-drop
// Update peer-index for (peerId, adv.fileId) using adv.presence.
}
API Reference
encodeAdvertisePayload(payload: ChunkAdvertisePayload): Uint8Array.decodeAdvertisePayload(bytes: Uint8Array): ChunkAdvertisePayload.- Errors:
DMP.BITSET_LENGTH_MISMATCH,DMP.BITSET_PADDING_NONZERO,DMP.CODEC_MISMATCH,DMP.ENVELOPE_MALFORMED.
Implementation Notes
The encode-side cross-check between bitLength (recovered from the framed presence bitset) and totalChunks is intentional defence-in-depth: it stops a bug in the issuer from emitting an advertisement whose receivers would later reject. Generations are validated up to 0xFFFF_FFFF at the schema layer; wrap-detection logic lives in generation/monotonic.ts. The unwrapWire helper passes through bare nulls / non-objects so the schema is the single source of "this isn't a valid envelope" rejection — every malformed input ends up at the schema gate, not at a JS-level type error.