diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d76f2c7 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12 @@ +{ + "name": "cloudflare-pds", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cloudflare-pds", + "version": "0.1.0" + } + } +} diff --git a/scripts/setup.js b/scripts/setup.js index 2907ea7..1e44223 100644 --- a/scripts/setup.js +++ b/scripts/setup.js @@ -149,7 +149,32 @@ function base58btcEncode(bytes) { return result } -// === CBOR ENCODING (minimal for PLC operations) === +// === CBOR ENCODING (dag-cbor compliant for PLC operations) === + +function cborEncodeKey(key) { + // Encode a string key to CBOR bytes (for sorting) + const bytes = new TextEncoder().encode(key) + const parts = [] + const mt = 3 << 5 // major type 3 = text string + if (bytes.length < 24) { + parts.push(mt | bytes.length) + } else if (bytes.length < 256) { + parts.push(mt | 24, bytes.length) + } else if (bytes.length < 65536) { + parts.push(mt | 25, bytes.length >> 8, bytes.length & 0xff) + } + parts.push(...bytes) + return new Uint8Array(parts) +} + +function compareBytes(a, b) { + // dag-cbor: bytewise lexicographic order of encoded keys + const minLen = Math.min(a.length, b.length) + for (let i = 0; i < minLen; i++) { + if (a[i] !== b[i]) return a[i] - b[i] + } + return a.length - b.length +} function cborEncode(value) { const parts = [] @@ -172,9 +197,11 @@ function cborEncode(value) { encodeHead(4, val.length) for (const item of val) encode(item) } else if (typeof val === 'object') { - const keys = Object.keys(val).sort() - encodeHead(5, keys.length) - for (const key of keys) { + // dag-cbor: sort keys by their CBOR-encoded bytes (length first, then lexicographic) + const keys = Object.keys(val) + const keysSorted = keys.sort((a, b) => compareBytes(cborEncodeKey(a), cborEncodeKey(b))) + encodeHead(5, keysSorted.length) + for (const key of keysSorted) { encode(key) encode(val[key]) } @@ -289,11 +316,11 @@ async function createGenesisOperation(opts) { } async function deriveDidFromOperation(operation) { - const { sig, ...opWithoutSig } = operation - const encoded = cborEncode(opWithoutSig) + // DID is computed from the FULL operation INCLUDING the signature + const encoded = cborEncode(operation) const hash = await sha256(encoded) - // DID is base32 of first 24 bytes of hash - return 'did:plc:' + base32Encode(hash.slice(0, 24)) + // DID is base32 of first 15 bytes of hash (= 24 base32 chars) + return 'did:plc:' + base32Encode(hash.slice(0, 15)) } function base32Encode(bytes) {