diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 58b9583..e6f7fdd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,6 +22,39 @@ jobs: # skip themselves — this runs the offline suite only. - run: pytest + # The JavaScript port (js/): its tests replay fixtures that the Python + # implementation generated and must reach the same bytes and roots. No + # npm install: the package has no dependencies. Node 22 is the floor + # (exact large integers need JSON.parse's source-text access). + js: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node-version: ["22", "24"] + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 + with: + node-version: ${{ matrix.node-version }} + - run: npm test + working-directory: js + + # The fixtures above must be what the Python code in src/ produces today: + # a change to the wire format that leaves them stale fails here, until + # `python3 js/tools/make_fixtures.py` is rerun. The bee and feeds extras + # bring swarmfs with keccak and signing (coincurve): the Swarm-addressed + # fixtures need the one, the signed feed updates the other. + js-fixtures: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + - run: pip install -e ".[bee,feeds]" + - run: python3 js/tools/make_fixtures.py --check + # Unused and shadowed names (review 2026-10, question 9): in loopmarket a # second definition of a function silently replaced the first and changed # what users approve; pyflakes names that line. Pinned, so a new pyflakes diff --git a/README.md b/README.md index 5c6a604..8d3e71c 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,9 @@ store = swarm_store("my-notes", owner=addr) # follow someone else's feed - **[Reference](docs/REFERENCE.md)** — compact and definition-first: every export, signature, error, and extra in tables, pinned against the code by `tests/test_reference.py`. The right document to hand to an AI agent. +- **[JavaScript port](js/README.md)** — early: the same store for Node and + browsers (including Freedom Browser's `window.swarm`), wire-compatible + with this package and tested against fixtures it generates. ## Testing diff --git a/js/README.md b/js/README.md new file mode 100644 index 0000000..d1c0c77 --- /dev/null +++ b/js/README.md @@ -0,0 +1,278 @@ +# recordstore for JavaScript + +A versioned key → record store over content-addressed storage such as +[Ethereum Swarm](https://www.ethswarm.org/): the JavaScript port of the +[Python recordstore](../README.md), and wire-compatible with it. A store +written by either reads in the other, and equal content gives the same root +in both: byte for byte, under SHA-256 or Swarm addressing. + +**Status: early (0.1.0, not on npm yet).** The core is complete and tested +against the Python implementation: records, staging, commits, sorted +iteration, diff, three-way merge, and reconciling a moved pointer. Stores for +memory, a Bee node, and Freedom Browser's `window.swarm`; the store's latest +root in a Swarm feed (`SwarmFeedPointer`); and proofs anyone can check with +no store: a key's record or its absence, everything under a prefix, a later +root extending an earlier one, a feed's signed updates. Not yet ported: +history and undo, local-first storage. + +Plain JavaScript modules with JSDoc types and no npm dependencies, so a page +can import it with relative URLs only, including a page published on Swarm. +Two files are vendored, the same copies swarmlite uses: js-sha3 0.9.3 for +keccak and @noble/secp256k1 2.3.0 for signing feed updates (see their +`PROVENANCE.md` under [vendor/](vendor/)). +Node 22 or later, or any current browser (SHA-256 addressing uses Web Crypto, +which browsers offer only in secure contexts). + +```js +import { RecordStore, MemoryBytesStore } from './recordstore/js/src/index.js'; + +const store = new RecordStore(new MemoryBytesStore()); +store.put('users/alice', { name: 'Alice', role: 'admin' }); +store.put('users/bob', { name: 'Bob' }); +const root = await store.commit(); // one reference names this version + +await store.get('users/alice'); // { name: 'Alice', role: 'admin' } +for await (const key of store.keys('users/')) console.log(key); + +const snapshot = RecordStore.at(root, store.blobs); // read-only, frozen +``` + +## Reading and writing on Swarm + +```js +import { RecordStore, BeeBytesStore } from './recordstore/js/src/index.js'; + +// Read any published store: no postage batch needed. +const pack = RecordStore.at( + '67f6be2688e5fb7ca85a463c3d2929dad2b177bb3edb6bb910e8fa110598cff3', + new BeeBytesStore('http://localhost:1633'), +); +await pack.get('abbey'); + +// Write: name a postage batch of the node's. +const blobs = new BeeBytesStore('http://localhost:1633', { batch: '' }); +``` + +Every blob read is checked against its reference by recomputing its Swarm +address, and every write against the reference the node returns. Writes go up +with redundancy level 1 for a blob that fits one chunk and 0 above, as the +Python store does, so that the node's reference is the plain content +address. A store written with erasure coding by some other tool reads with +`verify: false`. + +Measured on 8 November 2026: OntoDAG's `space` pack (5,134 records, written +by Python) listed in full from Swarm through the Ant node inside Freedom +Browser, cold, in 391 s, every blob checked (`test/live.test.js`). Listing +everything walks every trie node; a single `get` touches only the nodes on +its key's path. + +## A store behind a feed + +A root names one version; a feed gives a store a stable address that moves +with every commit, as Python's `swarm_store` and `SwarmFeedPointer` do. The +two implementations read each other's feeds. + +```js +import { swarmStore } from './recordstore/js/src/index.js'; + +// Publish your own: the feed is owned by your key; writes need a batch. +const mine = await swarmStore('my-notes', { url: 'http://localhost:1633', signer: '', batch: '' }); +mine.put('note/1', 'hello'); +await mine.commit(); // uploads the blobs, then the signed feed update + +// Follow someone else's: read-only, by their address. +const theirs = await swarmStore('loop-twonode-1791730596', { owner: '300f4daa2653102abde4c66f75ea512c0bb81571' }); +for await (const [key, record] of theirs.items()) console.log(key); +``` + +`SwarmFeedPointer(urlOrTransport, topicName, {signer | owner, batch})` is +the pointer by itself; `RecordStore.open(blobs, {pointer})` puts any store +behind it. Light-node feed lookups are unreliable, so, as in Python, it +never trusts one: a read-your-writes cache, a monotonic index floor for +writes, the tip found by probing the update chunks themselves, retries +with backoff that never go back to an older index, and every update +checked against the owner's signature. `compareAndSet` (used by +`commit({reconcile: true})`) is best effort: a feed cannot claim an index +atomically. `pointer.update(i)` and `pointer.updates()` return a feed's +signed updates as envelopes that `verifyFeedUpdate(envelope, owner, topic)` +checks with no node; `verifyEquivocation` proves an owner signed two roots +at one index. Signatures are deterministic (RFC 6979, low S), so the same +key, update and timestamp give the same bytes as Python. + +Live on 8 November 2026, through Swarm Desktop's Bee (`test/live.test.js`): +loopmarket's test maker book, a store Python keeps behind a feed, followed +to its root with its 8 records (12 s); the OntoDAG page's feed read with its +11 updates, the latest verified offline; and a store published behind a new +feed from JavaScript, then followed from JavaScript and from Python, which +read the same root and records and verified both updates. + +## Proofs + +A root is a commitment to a whole store, so a store can prove statements +about it that anyone holding only the root can check: no store, no network, +no trust in the prover. An envelope carries the raw trie nodes (and values); +the verifier recomputes their references and replays the walk. The trie is +canonical, so a key has one possible place under a root, and absence is as +provable as presence. + +```js +const proof = await store.prove('users/alice'); // inclusion, or absence +await verifyProof(proof, root); // the record, or ABSENT + +const all = await store.provePrefix('users/'); // every record under it, and no others +await verifyPrefix(all, root); // a Map, proven complete + +const grew = await store.proveExtension(oldRoot, ['log/']); // nothing under log/ removed or changed +await verifyExtension(grew, oldRoot, root); // the prefixes proven +``` + +Each envelope is checked before it is handed out, and a verifier rejects +with `ProofError`. The formats are Python's (`recordstore-trie-proof`, +`-prefix-proof`, `-extension-proof`, version 1), and the same store gives +the same envelope in both languages: the fixtures require JavaScript's +proofs to equal Python's, field for field. Verifiers return promises, +because Web Crypto's SHA-256 is asynchronous. + +Live on 8 November 2026: proofs about OntoDAG's `space` pack, made through +Bee in 0.4 s, that `abbey` holds its record (4 nodes, 8.4 KB as JSON), that +`abbeyz` is absent (8.2 KB), and of the 11 records under `ab` (13 KB), all +three accepted by Python's verifiers. + +### In Freedom Browser + +A page in Freedom cannot reach a node's HTTP API; it has `window.swarm`. + +```js +import { RecordStore, SwarmProviderBytesStore } from './recordstore/js/src/index.js'; +const blobs = new SwarmProviderBytesStore(window.swarm); +``` + +Reads use `swarm_readData` and count against Freedom's per-site read budget. +Any feed reads through `ProviderFeedTransport`, which fetches each update +with `swarm_readSingleOwnerChunk` and checks it again here: + +```js +const pointer = new SwarmFeedPointer(new ProviderFeedTransport(window.swarm), topic, { owner }); +const store = await RecordStore.open(blobs, { pointer }); +``` + +Writing a feed with your own key from a page waits for Freedom to accept +chunks a page signed (freedom-browser#647); until then `set` refuses. +Writes split each blob into chunks as Bee does and publish them with +`swarm_publishChunk`, so the references are Bee's and Python's. The first +write asks for access, and the store's first chunk goes out alone: Freedom +asks before each publish until the user ticks "Always allow this site", so +one approval then covers the rest of a commit (chunks sent in parallel from +the start each waited on a prompt: five for one commit). + +Checked in Freedom built from main (0.8.9-dev) on 8 November 2026 with +`test/freedom/freedom_check.cjs`, which runs `test/freedom/provider-check.html` +in a throwaway profile and answers Freedom's prompts: + +| | Freedom's node | Result | +|---|---|---| +| Read: 2 records and the 11 keys under a prefix of OntoDAG's `space` pack | its own Ant, just started | 65 reads, 13.5 s, one retry while the node found peers | +| The same read | Swarm Desktop's Bee | 0.4 s | +| Write a 3-record store (9 chunks, one over 4 KB), then read it back | Swarm Desktop's Bee | the root computed offline, read back equal; 3.1 s, one connect and one publish prompt | +| The same write | its own Ant, no postage batch | refused with `node-not-ready` (4900); the commit failed and left the root as it was | +| Follow loopmarket's test maker book through its feed | its own Ant | the same root as through Bee, 8 records; 8 update and 12 blob reads, 8.0 s | + +Writing through Freedom's own Ant node is not tested yet: its node needs a +funded wallet and a postage batch. Expect writes there to be slower: one +call per chunk, several seconds each through Ant in swarmtyp's measurements. + +## The API + +Shaped like the Python one, but asynchronous and JavaScript-idiomatic: + +| | | +|---|---| +| `new RecordStore(blobs, {root, pointer, readonly, nodeCacheSize})` | a store at `root` (null: empty) | +| `await RecordStore.open(blobs, {pointer})` | a store at its pointer's current root | +| `RecordStore.at(root, blobs)` | a read-only snapshot | +| `store.put(key, value)` | stage a write (validated and copied now) | +| `await store.delete(key)` | stage a removal; `false` if there is no such record | +| `await store.get(key)` | the record (a fresh copy), or `undefined` if absent; records can be `null` | +| `await store.has(key)` | Python's `contains` | +| `await store.getMany(keys)` | a `Map` of the records present, read together | +| `for await (const k of store.keys(prefix))` | sorted keys, staged changes included | +| `for await (const [k, v] of store.items(prefix))` | sorted records | +| `for await (const [k, mine, theirs] of store.diff(otherRoot))` | what differs; a missing side is `ABSENT` | +| `await store.commit({reconcile, resolver, retries})` | write, return the new root, move the pointer | +| `await RecordStore.merge(blobs, base, ours, theirs, resolver)` | three-way merge; `MergeConflict` without a resolver | +| `store.refsUnder(prefix)` | every blob the keys under `prefix` need (for keeping them alive) | +| `await swarmStore(topic, {url, signer \| owner, batch})` | a store on Swarm behind its feed | +| `new SwarmFeedPointer(urlOrTransport, topic, {signer \| owner, batch})` | the feed pointer: `get`, `set`, `compareAndSet`, `update(i)`, `updates()` | +| `verifyFeedUpdate(envelope, owner, topic)` | `{index, root, timestamp}` of a signed update, checked offline; `ProofError` otherwise | +| `await store.prove(key)` / `verifyProof(proof, root)` | a key's record or its absence, proven | +| `await store.provePrefix(prefix)` / `verifyPrefix(proof, root)` | every record under a prefix, proven complete (a `Map`) | +| `await store.extends(base, prefixes)` | does the root keep every record `base` held under the prefixes? | +| `await store.proveExtension(base, prefixes)` / `verifyExtension(proof, base, root)` | that it does, proven | + +Errors: `RecordUnavailable` when a record exists but a blob on its path cannot +be read right now (never reported as absence), `MergeConflict`, +`BlobNotFoundError`, `VerificationError`. + +Bytes stores: `MemoryBytesStore({addressing: 'sha256' | 'swarm'})`, +`BeeBytesStore(url, {batch, verify, deferred, concurrency})`, +`SwarmProviderBytesStore(provider, {verify, concurrency})`. Anything with +async `put(bytes) → ref` and `get(ref) → bytes` works, optionally with +`putMany` and `getMany` (a `Map`). `MemoryPointer` is a pointer with +compare-and-set for writers in one process. Feed transports: +`BeeFeedTransport(url, {batch})` and `ProviderFeedTransport(provider)`, or +anything with `readSoc(address)` and `writeSoc(owner, identifier, signature, +data, batch)`, optionally `feedHead(owner, topic, after)`. + +## Compatibility with Python + +The canonical encoding is Python's `json.dumps(sort_keys=True, +separators=(",", ":"), ensure_ascii=False, allow_nan=False)`, reproduced byte +for byte: keys sorted by code point (not JavaScript's UTF-16 order), the same +string escapes, Python's float formatting. Where the languages differ: + +- **Numbers.** JavaScript has one number type. A safe integer is written as an + integer, so a Python float such as `1.0`, read and written back from + JavaScript, becomes `1`: a different blob, as the Python guide warns. Other + numbers are written as Python writes floats (`1e-05`, `1e+16`). +- **Large integers.** A `BigInt` is written as an integer of any size, and + reading gives a `BigInt` back for an integer too large for a number, where + the engine exposes the literal's source text (Node 21+, Chrome 114+). + Elsewhere such an integer reads as the nearest number. +- **Lone surrogates** in keys or strings are refused, as Python refuses to + encode them; `TextEncoder` would otherwise turn them into U+FFFD silently. +- **Not JSON:** `undefined`, functions, symbols, `Date`, `Map` and class + instances are refused rather than converted. + +## Tests + +```sh +npm test # Node's own runner, no install +RECORDSTORE_BEE_URL=http://localhost:1633 npm test # also read live stores and feeds +RECORDSTORE_BEE_URL=... RECORDSTORE_BEE_BATCH= npm test # and write one (spends stamps) + +# in real Freedom Browser, from a freedom-browser checkout beside this repo +node test/freedom/freedom_check.cjs # read through Freedom's own node +node test/freedom/freedom_check.cjs --bee=http://127.0.0.1:1633 --write # spends stamps +``` + +`test/fixtures/` is written by `tools/make_fixtures.py`, which runs the Python +implementation in `../src` (not an installed copy) and records canonical +encodings, Swarm addresses, the root after every commit of scripted and random +stores under both addressings, three-way merges, proofs (key, prefix and +extension envelopes, their verdicts, and tampered ones Python refuses), and feeds: topics, owners, +update addresses, signed updates written by swarmfs's own code (so JavaScript +must reproduce the signatures byte for byte), and updates Python refuses. It +needs swarmfs with signing (`pip install "recordstore[bee,feeds]"`). The JavaScript tests +replay the same operations and must reach the same bytes and roots. After a +change to the Python format, `npm run fixtures`; `npm run fixtures:check` +fails while the fixtures are stale. + +## Not yet ported + +- History, undo and redo (`FilePointer`'s timeline). +- Local-first storage. + +## License + +BSD-3-Clause, as the Python package. The vendored js-sha3 and +noble-secp256k1 are MIT. diff --git a/js/package.json b/js/package.json new file mode 100644 index 0000000..8deac92 --- /dev/null +++ b/js/package.json @@ -0,0 +1,41 @@ +{ + "name": "recordstore", + "version": "0.1.0", + "private": true, + "description": "A versioned key -> record store over content-addressed storage such as Ethereum Swarm; wire-compatible JavaScript port of the Python recordstore", + "type": "module", + "main": "src/index.js", + "exports": { + ".": "./src/index.js" + }, + "files": [ + "src", + "vendor" + ], + "engines": { + "node": ">=22" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/petfold/recordstore.git", + "directory": "js" + }, + "homepage": "https://github.com/petfold/recordstore/tree/main/js#readme", + "keywords": [ + "swarm", + "ethswarm", + "key-value", + "content-addressed", + "merkle", + "trie", + "versioned", + "decentralized" + ], + "license": "BSD-3-Clause", + "author": "Peter Foldiak", + "scripts": { + "test": "node --test test/*.test.js", + "fixtures": "python3 tools/make_fixtures.py", + "fixtures:check": "python3 tools/make_fixtures.py --check" + } +} diff --git a/js/src/address.js b/js/src/address.js new file mode 100644 index 0000000..591d5c2 --- /dev/null +++ b/js/src/address.js @@ -0,0 +1,129 @@ +// Content addresses: SHA-256 (the default of MemoryBytesStore) and Swarm's +// own reference (BMT chunk hashes over Bee's chunk tree), ported from +// swarmfs's `bmt` and `splitter` modules. +// +// Swarm tree format (Bee's pkg/file/splitter): +// - a leaf is span (8 bytes, little-endian) + payload, payload <= 4096 bytes; +// - an intermediate is span + up to 128 child references (32 bytes each), +// span being the content length beneath it; +// - a level with a single entry is promoted, never wrapped; +// - empty content is one chunk with span 0 and an empty payload. +// +// This is the reference of a *plain* upload. A blob over one chunk uploaded +// with erasure coding gets another reference; `redundancyFor` names the level +// to upload with so that the node's reference is this one. + +import { keccak256Bytes } from '../vendor/js-sha3/index.js'; +import { concatBytes, fromHex, toHex } from './bytes.js'; + +export const CHUNK_SIZE = 4096; +const SEGMENT_SIZE = 32; +const REF_SIZE = 32; +const BRANCHES = CHUNK_SIZE / REF_SIZE; // 128 + +/** keccak256 of bytes, as bytes. */ +export { keccak256Bytes }; + +/** Root of the binary Merkle tree over a payload's 32-byte segments (zero-padded to 4096). */ +export function bmtRoot(payload) { + if (payload.length > CHUNK_SIZE) throw new RangeError(`payload of ${payload.length} bytes exceeds a chunk`); + let level = []; + for (let i = 0; i < CHUNK_SIZE; i += SEGMENT_SIZE) { + const seg = new Uint8Array(SEGMENT_SIZE); + seg.set(payload.subarray(i, Math.min(i + SEGMENT_SIZE, payload.length))); + level.push(seg); + } + while (level.length > 1) { + const next = []; + for (let i = 0; i < level.length; i += 2) next.push(keccak256Bytes(concatBytes(level[i], level[i + 1]))); + level = next; + } + return level[0]; +} + +/** Little-endian 8-byte span. */ +export function encodeSpan(span) { + const out = new Uint8Array(8); + let n = BigInt(span); + for (let i = 0; i < 8; i++) { + out[i] = Number(n & 0xffn); + n >>= 8n; + } + return out; +} + +/** BMT address of a content-addressed chunk (span + payload). */ +export function chunkAddress(chunk) { + return keccak256Bytes(concatBytes(chunk.subarray(0, 8), bmtRoot(chunk.subarray(8)))); +} + +/** + * Split data into Swarm chunks as Bee does. + * @param {Uint8Array} data + * @returns {{root: string, chunks: {address: string, payload: Uint8Array, span: number}[]}} + * the root reference (hex) and every chunk of the tree, leaves first, each + * with the payload and span to upload it with. + */ +export function splitData(data) { + const chunks = []; + const store = (payload, span) => { + const address = toHex(chunkAddress(concatBytes(encodeSpan(span), payload))); + chunks.push({ address, payload, span }); + return address; + }; + if (data.length === 0) return { root: store(new Uint8Array(0), 0), chunks }; + + let level = []; // [reference hex, content length beneath it] + for (let i = 0; i < data.length; i += CHUNK_SIZE) { + const payload = data.subarray(i, i + CHUNK_SIZE); + level.push([store(payload, payload.length), payload.length]); + } + while (level.length > 1) { + const parents = []; + for (let i = 0; i < level.length; i += BRANCHES) { + const group = level.slice(i, i + BRANCHES); + if (group.length === 1) { + parents.push(group[0]); // promote, never wrap + continue; + } + const span = group.reduce((sum, [, size]) => sum + size, 0); + const refs = new Uint8Array(group.length * REF_SIZE); + group.forEach(([ref], j) => refs.set(fromHex(ref), j * REF_SIZE)); + parents.push([store(refs, span), span]); + } + level = parents; + } + return { root: level[0][0], chunks }; +} + +/** Swarm's reference for `data` (plain upload), hex. */ +export function swarmRef(data) { + return splitData(data).root; +} + +/** SHA-256 of `data`, hex (MemoryBytesStore's references, as in Python). */ +export async function sha256Ref(data) { + const digest = await globalThis.crypto.subtle.digest('SHA-256', data); + return toHex(new Uint8Array(digest)); +} + +/** + * The redundancy level to upload a blob of `size` bytes with so that the + * node's reference is `swarmRef`'s: 1 while it fits one chunk (copies of the + * chunk, same reference), 0 above (parity would change the reference). + */ +export function redundancyFor(size) { + return size <= CHUNK_SIZE ? 1 : 0; +} + +/** + * An addressing scheme: 'sha256', 'swarm', or a function bytes -> reference + * (or a promise of one). + * @param {'sha256' | 'swarm' | ((data: Uint8Array) => string | Promise)} addressing + */ +export function resolveAddressing(addressing) { + if (typeof addressing === 'function') return addressing; + if (addressing === 'sha256') return sha256Ref; + if (addressing === 'swarm') return swarmRef; + throw new TypeError(`unknown addressing ${JSON.stringify(addressing)}; use 'sha256', 'swarm' or a function`); +} diff --git a/js/src/bytes.js b/js/src/bytes.js new file mode 100644 index 0000000..3fd7ddd --- /dev/null +++ b/js/src/bytes.js @@ -0,0 +1,98 @@ +// Byte, hex and text helpers shared by the other modules. +// +// Trie keys are handled as lowercase hex of their UTF-8 bytes. Hex keeps +// byte order under plain string comparison (so sorted hex is sorted UTF-8 is +// sorted code points, the order Python uses), slices on byte boundaries at +// even offsets, and is the form trie nodes store prefixes in anyway. + +const encoder = new TextEncoder(); +const decoder = new TextDecoder('utf-8', { fatal: true }); + +const HEX = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0')); + +/** Lowercase hex of a byte array. */ +export function toHex(bytes) { + let out = ''; + for (let i = 0; i < bytes.length; i++) out += HEX[bytes[i]]; + return out; +} + +/** Bytes of an even-length hex string (either case). */ +export function fromHex(hex) { + if (typeof hex !== 'string' || hex.length % 2 !== 0 || /[^0-9a-fA-F]/.test(hex)) { + throw new TypeError(`not a hex string: ${String(hex).slice(0, 80)}`); + } + const out = new Uint8Array(hex.length / 2); + for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(2 * i, 2 * i + 2), 16); + return out; +} + +/** Concatenate byte arrays. */ +export function concatBytes(...parts) { + let size = 0; + for (const p of parts) size += p.length; + const out = new Uint8Array(size); + let at = 0; + for (const p of parts) { + out.set(p, at); + at += p.length; + } + return out; +} + +/** + * Does `s` contain a lone surrogate? Python cannot encode one to UTF-8 and + * refuses; TextEncoder would silently write U+FFFD instead, which would + * give a different key or value than the caller passed. So we refuse too. + */ +export function isWellFormed(s) { + if (typeof s.isWellFormed === 'function') return s.isWellFormed(); + return !/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(? equal bytes, identical to Python's +// +// json.dumps(obj, sort_keys=True, separators=(",", ":"), +// ensure_ascii=False, allow_nan=False).encode("utf-8") +// +// Content addressing makes byte identity a correctness requirement: a value +// encoded differently here than in Python would get a different reference, +// and the store a different root, for the same content. +// +// Numbers are the one place the two languages differ. Python has int and +// float; JavaScript has one number type (plus BigInt). The rules: +// +// - A safe integer (|n| <= 2**53 - 1) is written as an integer, like a +// Python int. So a Python float such as 1.0 ("1.0"), read here, comes back +// as 1 and would be written "1": different bytes. Store what JSON +// represents faithfully, as the Python guide says. +// - Any other finite number is written as Python writes a float (its +// shortest round-trip repr, exponent from 1e16 and below 1e-4): 0.1, +// 1e-05, 1e+16, 9007199254740992.0. +// - A BigInt is written as an integer, like a Python int of any size, and +// `parseJson` gives back a BigInt for an integer literal that a number +// cannot hold exactly (where the engine exposes the literal's source text). +// - NaN and the infinities have no JSON form and are refused, as in Python. + +import { compareCodePoints, fromUtf8, isWellFormed, utf8 } from './bytes.js'; + +/** Canonical JSON text of a JSON-compatible value. */ +export function canonicalJson(value) { + return encode(value, 0); +} + +/** Canonical bytes (UTF-8 of `canonicalJson`). */ +export function canonicalBytes(value) { + return utf8(canonicalJson(value)); +} + +const MAX_DEPTH = 1000; // Python's default recursion limit is of this order + +function encode(v, depth) { + if (depth > MAX_DEPTH) throw new RangeError('value nested too deeply'); + if (v === null) return 'null'; + switch (typeof v) { + case 'boolean': + return v ? 'true' : 'false'; + case 'number': + return encodeNumber(v); + case 'bigint': + return v.toString(); + case 'string': + return encodeString(v); + case 'object': + break; + default: + throw new TypeError(`not JSON-serializable: ${typeof v}`); + } + if (Array.isArray(v)) { + const parts = []; + for (let i = 0; i < v.length; i++) { + if (!(i in v) || v[i] === undefined) throw new TypeError(`not JSON-serializable: array hole or undefined at index ${i}`); + parts.push(encode(v[i], depth + 1)); + } + return `[${parts.join(',')}]`; + } + const proto = Object.getPrototypeOf(v); + if (proto !== Object.prototype && proto !== null) { + throw new TypeError(`not JSON-serializable: ${Object.prototype.toString.call(v)} (only plain objects, arrays and primitives)`); + } + if (Object.getOwnPropertySymbols(v).length) throw new TypeError('not JSON-serializable: symbol keys'); + const keys = Object.keys(v).sort(compareCodePoints); + const parts = []; + for (const k of keys) { + if (v[k] === undefined) throw new TypeError(`not JSON-serializable: undefined at key ${JSON.stringify(k)}`); + parts.push(`${encodeString(k)}:${encode(v[k], depth + 1)}`); + } + return `{${parts.join(',')}}`; +} + +function encodeString(s) { + // JSON.stringify escapes exactly what Python's encoder escapes with + // ensure_ascii=False: " \ and the C0 controls, \b \f \n \r \t short and the + // rest as lowercase \u00XX; U+007F, U+2028 and U+2029 stay raw in both. + // The one difference is a lone surrogate, which Python refuses to encode. + if (!isWellFormed(s)) throw new TypeError('string contains a lone surrogate (not encodable as UTF-8)'); + return JSON.stringify(s); +} + +function encodeNumber(x) { + if (!Number.isFinite(x)) throw new RangeError(`${x} has no canonical JSON form`); + if (Number.isSafeInteger(x)) return String(x); // String(-0) === "0" + return pythonFloatRepr(x); +} + +/** + * Python's `repr(float)`: the shortest digits that round-trip (which + * `toExponential()` also gives), written positionally when the decimal + * exponent is in [-4, 16) and in exponent form otherwise, with a sign and at + * least two exponent digits; a positional integral value gets ".0". + */ +export function pythonFloatRepr(x) { + let s = x.toExponential(); + let sign = ''; + if (s[0] === '-') { + sign = '-'; + s = s.slice(1); + } + const [mantissa, expText] = s.split('e'); + const digits = mantissa.replace('.', ''); + const exp = parseInt(expText, 10); + const decpt = exp + 1; // digits are 0.d1d2... x 10**decpt + if (decpt <= -4 || decpt > 16) { + const m = digits.length > 1 ? `${digits[0]}.${digits.slice(1)}` : digits; + const e = String(Math.abs(exp)).padStart(2, '0'); + return `${sign}${m}e${exp < 0 ? '-' : '+'}${e}`; + } + if (decpt <= 0) return `${sign}0.${'0'.repeat(-decpt)}${digits}`; + if (decpt >= digits.length) return `${sign}${digits}${'0'.repeat(decpt - digits.length)}.0`; + return `${sign}${digits.slice(0, decpt)}.${digits.slice(decpt)}`; +} + +const INTEGER = /^-?(?:0|[1-9][0-9]*)$/; + +function reviver(_key, value, context) { + if (typeof value === 'number' && !Number.isSafeInteger(value) && + context && typeof context.source === 'string' && INTEGER.test(context.source)) { + return BigInt(context.source); + } + return value; +} + +/** + * JSON.parse that keeps large integers exact: an integer literal beyond + * 2**53 - 1 becomes a BigInt, where the engine passes the literal's source + * text to revivers (Node 21+, Chrome 114+). Elsewhere it becomes the nearest + * number, as plain JSON.parse gives. + */ +export function parseJson(text) { + return JSON.parse(text, reviver); +} + +// The record envelope, as Python's `_encode_value` / `_decode_value`. +const SCHEMA_VERSION = 1; + +/** Bytes of a record value as stored in its blob. */ +export function encodeValue(value) { + return canonicalBytes({ rsv: SCHEMA_VERSION, val: value }); +} + +/** The record value a blob holds. */ +export function decodeValue(bytes) { + const obj = parseJson(fromUtf8(bytes)); + if (obj === null || typeof obj !== 'object' || obj.rsv !== SCHEMA_VERSION) { + throw new Error(`unsupported record schema version: ${JSON.stringify(obj && obj.rsv)}`); + } + return obj.val; +} + +/** A deep copy that is also a validity check: what a stored value reads back as. */ +export function canonicalCopy(value) { + return parseJson(canonicalJson(value)); +} diff --git a/js/src/feeds.js b/js/src/feeds.js new file mode 100644 index 0000000..65c9c29 --- /dev/null +++ b/js/src/feeds.js @@ -0,0 +1,503 @@ +// Swarm feeds as the store's pointer: SwarmFeedPointer, ported from the +// Python package (which builds on swarmfs's feeds), and feed updates +// verifiable offline. +// +// The format is bee-js's: +// - a topic name is hashed: keccak256 of its UTF-8; +// - the identifier of update `index` is keccak256(topic ‖ index as 8 bytes +// big-endian); +// - the update is a single-owner chunk at keccak256(identifier ‖ owner): +// identifier (32) ‖ signature (65) ‖ span (8) ‖ payload, the payload +// being the writer's timestamp (8 bytes big-endian, unix seconds) ‖ root; +// - the signature is Ethereum's personal-message signature, r ‖ s ‖ v with +// v in {27, 28}, over keccak256(identifier ‖ the wrapped chunk's address). +// +// Signing is RFC 6979 deterministic with low S (noble-secp256k1, as +// libsecp256k1 in Python), so the same key, update and timestamp give the +// same bytes in both languages. + +import { getPublicKey, signAsync, Signature } from '../vendor/noble-secp256k1/index.js'; +import { chunkAddress, encodeSpan, keccak256Bytes } from './address.js'; +import { concatBytes, fromBase64, fromHex, toHex, utf8 } from './bytes.js'; +import { VerificationError } from './stores.js'; +import { ProofError } from './proofs.js'; + +export const FEED_UPDATE_FORMAT = 'recordstore-feed-update'; + +const SOC_IDENTIFIER_SIZE = 32; +const SOC_SIGNATURE_SIZE = 65; +const SOC_SPAN_OFFSET = SOC_IDENTIFIER_SIZE + SOC_SIGNATURE_SIZE; // 97 +const SOC_PAYLOAD_OFFSET = SOC_SPAN_OFFSET + 8; // 105 +const ETH_PREFIX = utf8('\x19Ethereum Signed Message:\n32'); + +/** A transport's answer for a chunk or lookup: `status` 404 means absent, 500 an incomplete retrieval. */ +export class FeedTransportError extends Error { + constructor(message, status) { + super(message); + this.name = 'FeedTransportError'; + this.status = status; + } +} + +// -- addressing ------------------------------------------------------------- + +/** The 32-byte topic of a topic name: keccak256 of its UTF-8, always (as SwarmFeedPointer). */ +export function topicOf(name) { + if (typeof name !== 'string') throw new TypeError('a topic name is a string'); + return keccak256Bytes(utf8(name)); +} + +/** keccak256(topic ‖ index as 8 bytes big-endian). */ +export function feedIdentifier(topic, index) { + const idx = new Uint8Array(8); + new DataView(idx.buffer).setBigUint64(0, BigInt(index), false); + return keccak256Bytes(concatBytes(topic, idx)); +} + +/** Where a single-owner chunk lives: keccak256(identifier ‖ owner). */ +export function socAddress(identifier, owner) { + return keccak256Bytes(concatBytes(identifier, owner)); +} + +function strip0x(hex) { + return hex.startsWith('0x') || hex.startsWith('0X') ? hex.slice(2) : hex; +} + +/** 20 owner bytes from 40 hex (optional 0x, either case). */ +export function ownerBytes(owner) { + const h = strip0x(String(owner)).toLowerCase(); + if (!/^[0-9a-f]{40}$/.test(h)) throw new TypeError(`${JSON.stringify(owner)} is not a 20-byte owner address`); + return fromHex(h); +} + +function privateKeyBytes(key) { + const h = typeof key === 'string' ? strip0x(key).toLowerCase() : toHex(key); + if (!/^[0-9a-f]{64}$/.test(h)) throw new TypeError('a private key is 32 bytes (64 hex characters)'); + return fromHex(h); +} + +/** The address (20 bytes) of a secp256k1 private key: the last 20 bytes of keccak256 of the public key. */ +export function addressOf(privateKey) { + const pub = getPublicKey(privateKeyBytes(privateKey), false); // 0x04 ‖ x ‖ y + return keccak256Bytes(pub.subarray(1)).subarray(12); +} + +// -- signatures ------------------------------------------------------------- + +function messageDigest(digest32) { + return keccak256Bytes(concatBytes(ETH_PREFIX, digest32)); +} + +/** 65-byte r ‖ s ‖ v over the personal-message digest of `digest32`. */ +export async function signDigest(privateKey, digest32) { + const sig = await signAsync(messageDigest(digest32), privateKeyBytes(privateKey), { lowS: true }); + return concatBytes(sig.toCompactRawBytes(), new Uint8Array([27 + sig.recovery])); +} + +/** The 20-byte address whose key made `signature` over the personal-message digest of `digest32`. */ +export function recoverDigest(signature, digest32) { + if (signature.length !== 65) throw new VerificationError(`a signature is 65 bytes, got ${signature.length}`); + const v = signature[64]; + const recid = v === 27 || v === 28 ? v - 27 : v; + if (recid !== 0 && recid !== 1) throw new VerificationError(`unsupported recovery byte ${v}`); + let pub; + try { + pub = Signature.fromCompact(signature.subarray(0, 64)).addRecoveryBit(recid) + .recoverPublicKey(messageDigest(digest32)).toBytes(false); + } catch (e) { + throw new VerificationError(`signature recovers no key: ${e.message || e}`); + } + return keccak256Bytes(pub.subarray(1)).subarray(12); +} + +/** + * Check a single-owner chunk as a Bee node does on upload: its address + * derives from its identifier and `owner`, and its signature over + * (identifier, wrapped chunk address) recovers to `owner`. + */ +export function verifySoc(soc, owner, address) { + if (soc.length < SOC_PAYLOAD_OFFSET) throw new VerificationError(`single-owner chunk too short: ${soc.length} bytes`); + const identifier = soc.subarray(0, SOC_IDENTIFIER_SIZE); + const signature = soc.subarray(SOC_IDENTIFIER_SIZE, SOC_SPAN_OFFSET); + const cac = soc.subarray(SOC_SPAN_OFFSET); + if (toHex(socAddress(identifier, owner)) !== toHex(address)) { + throw new VerificationError('single-owner chunk does not match its address'); + } + const recovered = recoverDigest(signature, keccak256Bytes(concatBytes(identifier, chunkAddress(cac)))); + if (toHex(recovered) !== toHex(owner)) throw new VerificationError('feed update signature was not made by the feed owner'); +} + +/** + * The root and timestamp a feed update chunk publishes. Tolerates the other + * payload formats swarmfs reads: a bare reference, or a wrapped root chunk. + */ +export function payloadFromSoc(soc) { + if (soc.length < SOC_PAYLOAD_OFFSET) throw new VerificationError(`feed update chunk too short: ${soc.length} bytes`); + const payload = soc.subarray(SOC_PAYLOAD_OFFSET); + if (payload.length === 40 || payload.length === 72) { + return { reference: toHex(payload.subarray(8)), timestamp: Number(new DataView(payload.buffer, payload.byteOffset, 8).getBigUint64(0, false)) }; + } + if (payload.length === 32 || payload.length === 64) return { reference: toHex(payload), timestamp: null }; + return { reference: toHex(chunkAddress(soc.subarray(SOC_SPAN_OFFSET))), timestamp: null }; +} + +/** + * The feed update publishing `root` as update `index` of `topic` (bytes), + * signed with `privateKey`, as swarmfs (and bee-js) write it. + * @returns {Promise<{identifier: Uint8Array, signature: Uint8Array, data: Uint8Array, soc: Uint8Array, address: Uint8Array}>} + * `data` (span ‖ payload) is what Bee's POST /soc takes; `soc` is the whole chunk. + */ +export async function buildFeedUpdate(privateKey, topic, index, root, timestamp) { + const ts = new Uint8Array(8); + new DataView(ts.buffer).setBigUint64(0, BigInt(timestamp), false); + const payload = concatBytes(ts, fromHex(root)); + const data = concatBytes(encodeSpan(payload.length), payload); + const identifier = feedIdentifier(topic, index); + const signature = await signDigest(privateKey, keccak256Bytes(concatBytes(identifier, chunkAddress(data)))); + return { + identifier, signature, data, + soc: concatBytes(identifier, signature, data), + address: socAddress(identifier, addressOf(privateKey)), + }; +} + +// -- offline verification of published updates ---------------------------- + +function hexOwner(owner) { + const h = strip0x(String(owner)); + if (h.length !== 40) throw new ProofError(`${JSON.stringify(owner)} is not a 20-byte owner address`); + return h.toLowerCase(); +} + +/** A 32-byte topic in hex, or a topic name as SwarmFeedPointer hashes it. */ +function hexTopic(topic) { + const h = strip0x(String(topic)); + if (/^[0-9a-fA-F]{64}$/.test(h)) return h.toLowerCase(); + return toHex(topicOf(topic)); +} + +/** + * Check that `update` (an envelope from `SwarmFeedPointer.update`) is + * `owner`'s signed update of the feed `topic` at the index it claims, and + * return what it publishes. Raises ProofError on any mismatch. Needs no node. + * @returns {{index: number, root: string, timestamp: number}} + */ +export function verifyFeedUpdate(update, owner, topic) { + if (!update || typeof update !== 'object' || update.format !== FEED_UPDATE_FORMAT) throw new ProofError(`not a ${FEED_UPDATE_FORMAT} envelope`); + if (update.version !== 1) throw new ProofError(`unsupported feed update version ${JSON.stringify(update.version)}`); + const ownerHex = hexOwner(owner); + const topicHex = hexTopic(topic); + if (update.owner !== ownerHex || update.topic !== topicHex) throw new ProofError("the update is another owner's or another feed's"); + const { index } = update; + if (!Number.isSafeInteger(index) || index < 0) throw new ProofError('the update names no index'); + const ownerB = fromHex(ownerHex); + const address = socAddress(feedIdentifier(fromHex(topicHex), index), ownerB); + let wire; + try { + wire = fromHex(typeof update.soc === 'string' ? update.soc : ''); + verifySoc(wire, ownerB, address); + } catch { + throw new ProofError("the chunk is not the owner's signed update at this index"); + } + const payload = wire.subarray(SOC_PAYLOAD_OFFSET); + if (payload.length !== 40 && payload.length !== 72) throw new ProofError("the update's payload is not a timestamp and a root"); + return payloadToUpdate(index, payload); +} + +function payloadToUpdate(index, payload) { + const timestamp = Number(new DataView(payload.buffer, payload.byteOffset, 8).getBigUint64(0, false)); + return { index, root: toHex(payload.subarray(8)), timestamp }; +} + +/** + * Two updates by `owner` of one feed at one index publishing different + * roots: the owner equivocated, and this returns the index. ProofError when + * either does not verify, the indices differ, or the roots agree. + */ +export function verifyEquivocation(a, b, owner, topic) { + const ua = verifyFeedUpdate(a, owner, topic); + const ub = verifyFeedUpdate(b, owner, topic); + if (ua.index !== ub.index) throw new ProofError('updates at different indices are a sequence, not an equivocation'); + if (ua.root === ub.root) throw new ProofError('the two updates publish the same root'); + return ua.index; +} + +// -- transports ------------------------------------------------------------- + +/** + * Feeds over Bee's HTTP API (or Ant's): chunks from GET /chunks, uploads + * with POST /soc, and Bee's sequence lookup (GET /feeds) as a shortcut. + */ +export class BeeFeedTransport { + constructor(url, { batch = null, fetch: fetchFn } = {}) { + this.url = url.replace(/\/+$/, ''); + this.batch = batch; + this._fetch = fetchFn || globalThis.fetch.bind(globalThis); + } + + async readSoc(address) { + const res = await this._fetch(`${this.url}/chunks/${address}`); + if (!res.ok) throw new FeedTransportError(`GET /chunks/${address}: HTTP ${res.status}`, res.status); + return new Uint8Array(await res.arrayBuffer()); + } + + async writeSoc(owner, identifier, signature, data, batch = this.batch) { + if (!batch) throw new Error('writing a feed update to Bee needs a postage batch (the `batch` option)'); + const res = await this._fetch(`${this.url}/soc/${owner}/${identifier}?sig=${signature}`, { + method: 'POST', + headers: { 'swarm-postage-batch-id': batch, 'content-type': 'application/octet-stream' }, + body: data, + }); + if (!res.ok) throw new FeedTransportError(`POST /soc: HTTP ${res.status} ${await res.text().catch(() => '')}`, res.status); + } + + /** The latest index Bee's lookup finds, or null for an empty feed. */ + async feedHead(owner, topic, after = null) { + let url = `${this.url}/feeds/${owner}/${topic}?type=sequence`; + if (after !== null) url += `&after=${after}`; + const res = await this._fetch(url, { headers: { 'swarm-only-root-chunk': 'true' } }); + if (res.status === 404) return null; + if (!res.ok) throw new FeedTransportError(`GET /feeds: HTTP ${res.status}`, res.status); + await res.arrayBuffer().catch(() => {}); // only the headers matter + const index = res.headers.get('swarm-feed-index'); + return index ? Number(BigInt(`0x${index}`)) : null; + } +} + +/** + * Feeds through Freedom Browser's window.swarm. Reading works for any + * owner's feed: `swarm_readSingleOwnerChunk` returns the chunk with its + * signature, and the pointer checks it again here. Writing a feed with your + * own key waits for Freedom to accept chunks a page signed + * (freedom-browser#647). + */ +export class ProviderFeedTransport { + constructor(provider = globalThis.swarm) { + if (!provider || typeof provider.request !== 'function') throw new TypeError('no window.swarm provider (open the page in Freedom Browser)'); + this.provider = provider; + } + + async readSoc(address) { + let r; + try { + r = await this.provider.request({ method: 'swarm_readSingleOwnerChunk', params: { address } }); + } catch (e) { + const reason = e && e.data && e.data.reason; + if (reason === 'chunk_not_found') throw new FeedTransportError(`no chunk at ${address}`, 404); + if (reason === 'rate_limited') throw new FeedTransportError('read budget exhausted', 429); + throw e; + } + const payload = fromBase64(r.data); + return concatBytes(fromHex(strip0x(r.identifier)), fromHex(strip0x(r.signature)), encodeSpan(r.span ?? payload.length), payload); + } + + async writeSoc() { + throw new Error('writing a feed with your own key from a page waits for Freedom to accept chunks the page signed (freedom-browser#647)'); + } +} + +// -- the pointer ------------------------------------------------------------ + +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +const TRANSIENT = new Set([404, 429, 500]); + +/** + * The store's mutable "latest root" in a Swarm feed, as Python's + * SwarmFeedPointer: `set(root)` publishes a signed update, `get()` resolves + * the latest root. Light-node feed lookups are unreliable (404s, stale-early + * indexes), so it never trusts one: a read-your-writes cache, a monotonic + * index floor for writes, the tip found by probing the update chunks + * themselves, and retries with backoff that never go back to an older index. + * Every chunk read is verified against the owner. + * + * `transport` is a Bee API URL (or a BeeFeedTransport, ProviderFeedTransport, + * or anything with readSoc/writeSoc and optionally feedHead). Pass `signer` + * (private key hex) to read and write, or `owner` (address hex) to read. + * Writing to Bee also needs `batch`. `topic` is a name, hashed to the topic. + */ +export class SwarmFeedPointer { + constructor(transport, topic, { + signer = null, owner = null, batch = null, feedTtl = 15, maxLookupRetries = 15, + retryBackoff = 0.5, retryBackoffCap = 5, sleep: sleepFn = sleep, now = () => Date.now(), + } = {}) { + this._transport = typeof transport === 'string' ? new BeeFeedTransport(transport, { batch }) : transport; + this._topic = topicOf(topic); + if (signer) { + this._key = privateKeyBytes(signer); + this._owner = addressOf(this._key); + } else if (owner) { + this._key = null; + this._owner = ownerBytes(owner); + } else { + throw new TypeError('SwarmFeedPointer needs a signer (to read and write) or an owner address (read-only)'); + } + if (batch !== null) { + const b = strip0x(String(batch)).toLowerCase(); + if (!/^[0-9a-f]{64}$/.test(b)) throw new TypeError(`${JSON.stringify(batch)} is not a postage batch id`); + batch = b; + } + this._batch = batch; + this._ttl = feedTtl * 1000; + this._maxRetries = Math.max(1, maxLookupRetries); + this._backoff = retryBackoff * 1000; + this._backoffCap = retryBackoffCap * 1000; + this._sleep = sleepFn; + this._now = now; + // read-your-writes cache + monotonic index floor + this._cachedRef = null; + this._nextIndex = 0; + this._cacheExpiry = 0; + } + + /** The feed owner's address, hex (no 0x). */ + get owner() { + return toHex(this._owner); + } + + /** The 32-byte topic, hex. */ + get topic() { + return toHex(this._topic); + } + + async set(root) { + if (!this._key) throw new Error('SwarmFeedPointer.set needs a signer'); + let index; + if (this._nextIndex > 0) { + index = this._nextIndex; // a persistent writer's floor is authoritative + } else { + const probed = await this._probeLatestIndex(); + index = probed === null ? 0 : probed + 1; + } + const update = await buildFeedUpdate(this._key, this._topic, index, root, Math.floor(this._now() / 1000)); + await this._transport.writeSoc(this.owner, toHex(update.identifier), toHex(update.signature), update.data, this._batch); + this._cachedRef = root; + this._nextIndex = index + 1; + this._cacheExpiry = this._now() + this._ttl; + } + + async get() { + if (this._cachedRef !== null && this._now() < this._cacheExpiry) return this._cachedRef; + let delay = this._backoff; + for (let attempt = 0; attempt < this._maxRetries; attempt++) { + try { + const latest = await this._resolveLatestIndex(); + if (latest === null) return this._cachedRef; // empty feed (definitive) + const indexNext = latest + 1; + if (indexNext > this._nextIndex || this._cachedRef === null) { + const { reference } = await this._atIndex(latest); + this._cachedRef = reference; + this._nextIndex = indexNext; + this._cacheExpiry = this._now() + this._ttl; + return this._cachedRef; + } + if (indexNext === this._nextIndex) { + this._cacheExpiry = this._now() + this._ttl; // confirmed unchanged + return this._cachedRef; + } + // indexNext below the floor: a stale-early lookup; retry for a fresher one + } catch (e) { + if (!(e instanceof FeedTransportError && TRANSIENT.has(e.status))) throw e; + } + if (attempt < this._maxRetries - 1) { + await this._sleep(delay); + delay = Math.min(delay * 2, this._backoffCap); + } + } + return this._cachedRef; // last known, or null if never resolved + } + + async _getFresh() { + this._cacheExpiry = 0; + return this.get(); + } + + /** + * Best-effort compare-and-set for reconciling commits: true only if the + * feed still resolved to `expected` and `root` was written and read back. + * A feed cannot claim an index atomically (Bee accepts a second update at + * a used index), so two writers at the same index at the same moment can + * both believe they won; this narrows the window, it does not close it. + */ + async compareAndSet(expected, root) { + if ((await this._getFresh()) !== expected) return false; + await this.set(root); + return (await this._getFresh()) === root; + } + + async _resolveLatestIndex() { + const hint = this._nextIndex - 2; + if (hint >= 1 && typeof this._transport.feedHead === 'function') { + try { + const head = await this._transport.feedHead(this.owner, this.topic, hint); + if (head !== null && head !== undefined) return head; + } catch (e) { + if (!(e instanceof FeedTransportError && TRANSIENT.has(e.status))) throw e; + } + } + return this._probeLatestIndex(); + } + + /** Highest existing index (null: empty), by exponential + binary search over the update chunks. */ + async _probeLatestIndex() { + if (!(await this._socExists(0))) return null; + let lo = 0; + let hi = 1; + while (await this._socExists(hi)) { + lo = hi; + hi *= 2; + } + while (hi - lo > 1) { + const mid = Math.floor((lo + hi) / 2); + if (await this._socExists(mid)) lo = mid; + else hi = mid; + } + return lo; + } + + /** Does the update at `index` exist? 404 is the one definitive no; 500 (and 429) are retried. */ + async _socExists(index) { + let delay = this._backoff; + for (let attempt = 0; attempt < this._maxRetries; attempt++) { + try { + await this._atIndex(index); + return true; + } catch (e) { + if (!(e instanceof FeedTransportError)) throw e; + if (e.status === 404) return false; + if ((e.status !== 500 && e.status !== 429) || attempt === this._maxRetries - 1) throw e; + } + await this._sleep(delay); + delay = Math.min(delay * 2, this._backoffCap); + } + return false; + } + + _address(index) { + return socAddress(feedIdentifier(this._topic, index), this._owner); + } + + /** The verified update at `index`: {reference, timestamp}. */ + async _atIndex(index) { + const address = this._address(index); + const soc = await this._transport.readSoc(toHex(address)); + verifySoc(soc, this._owner, address); + return payloadFromSoc(soc); + } + + /** The update at `index` as a self-contained envelope that `verifyFeedUpdate` checks with no node. */ + async update(index) { + const soc = await this._transport.readSoc(toHex(this._address(index))); + return { format: FEED_UPDATE_FORMAT, version: 1, owner: this.owner, topic: this.topic, index, soc: toHex(soc) }; + } + + /** The updates from `start` up to `stop` (default: past the tip, found by probing), in index order. */ + async updates(start = 0, stop = null) { + if (stop === null) { + const tip = await this._probeLatestIndex(); + stop = tip === null ? 0 : tip + 1; + } + const out = []; + for (let i = start; i < stop; i++) out.push(await this.update(i)); + return out; + } +} diff --git a/js/src/index.js b/js/src/index.js new file mode 100644 index 0000000..9e707c6 --- /dev/null +++ b/js/src/index.js @@ -0,0 +1,24 @@ +// recordstore for JavaScript: a versioned key -> record store over a +// content-addressed bytes store, wire-compatible with the Python package. + +export { + RecordStore, MemoryPointer, MergeConflict, RecordUnavailable, ABSENT, DELETE, +} from './recordstore.js'; +export { + MemoryBytesStore, BeeBytesStore, SwarmProviderBytesStore, BlobNotFoundError, VerificationError, +} from './stores.js'; +export { + canonicalBytes, canonicalJson, parseJson, encodeValue, decodeValue, +} from './canonical.js'; +export { + swarmRef, sha256Ref, splitData, chunkAddress, bmtRoot, redundancyFor, CHUNK_SIZE, +} from './address.js'; +export { DEFAULT_NODE_CACHE_SIZE } from './trie.js'; +export { + SwarmFeedPointer, BeeFeedTransport, ProviderFeedTransport, FeedTransportError, + verifyFeedUpdate, verifyEquivocation, FEED_UPDATE_FORMAT, topicOf, feedIdentifier, socAddress, addressOf, +} from './feeds.js'; +export { + ProofError, verifyProof, verifyPrefix, verifyExtension, PROOF_FORMAT, PREFIX_FORMAT, EXTENSION_FORMAT, +} from './proofs.js'; +export { swarmStore } from './swarm.js'; diff --git a/js/src/proofs.js b/js/src/proofs.js new file mode 100644 index 0000000..41abf47 --- /dev/null +++ b/js/src/proofs.js @@ -0,0 +1,315 @@ +// Proofs about committed roots, verifiable with no store, ported from the +// Python package: +// +// - a key proof (format "recordstore-trie-proof"): a key's record, or its +// absence. The trie is canonical, so a key has exactly one possible place +// under a root, which is what makes absence provable; +// - a prefix proof ("recordstore-prefix-proof"): every record under a +// prefix, and no others; +// - an extension proof ("recordstore-extension-proof"): a later root holds +// every record an earlier root held under some prefixes, unchanged. +// +// An envelope carries the raw node and value blobs; a verifier recomputes +// their references (SHA-256 or Swarm, as the envelope names) and replays the +// walk from the root it trusts. Equal stores give equal envelopes in Python +// and JavaScript. Verifiers return promises (Web Crypto's SHA-256 is async). + +import { resolveAddressing } from './address.js'; +import { compareCodePoints, fromHex, hexUtf8, isWellFormed, utf8Hex } from './bytes.js'; +import { decodeValue } from './canonical.js'; +import { commonPrefix, Trie, TrieNode } from './trie.js'; + +export const PROOF_FORMAT = 'recordstore-trie-proof'; +export const PREFIX_FORMAT = 'recordstore-prefix-proof'; +export const EXTENSION_FORMAT = 'recordstore-extension-proof'; + +/** The same symbol as recordstore.js's ABSENT (Symbol.for). */ +const ABSENT = Symbol.for('recordstore.ABSENT'); + +/** A proof (or feed update) that does not verify against the given root. */ +export class ProofError extends Error { + constructor(message) { + super(message); + this.name = 'ProofError'; + } +} + +function envelope(proof, format) { + if (!proof || typeof proof !== 'object' || proof.format !== format) throw new ProofError(`not a ${format} envelope`); + if (proof.version !== 1) throw new ProofError(`unsupported proof version ${JSON.stringify(proof.version)}`); +} + +function addressing(name) { + if (name !== 'sha256' && name !== 'swarm') throw new ProofError(`unknown addressing ${JSON.stringify(name)}`); + return resolveAddressing(name); +} + +function blob(hex, what) { + try { + return fromHex(hex); + } catch { + throw new ProofError(`malformed ${what} bytes in proof`); + } +} + +function blobs(list, what) { + if (list === undefined || list === null) return []; + if (!Array.isArray(list)) throw new ProofError(`malformed ${what} bytes in proof`); + return list.map((h) => blob(h, what)); +} + +function decodeNode(bytes, label) { + try { + return Trie.decode(bytes); + } catch { + throw new ProofError(`${label} is not a valid trie node`); + } +} + +function keyText(hex) { + try { + return hexUtf8(hex); + } catch { + return hex; + } +} + +function prefixHex(prefix) { + if (!isWellFormed(prefix)) throw new TypeError('prefix contains a lone surrogate'); + return utf8Hex(prefix); +} + +/** + * The addressing name a proof envelope carries: the bytes store's, which + * must be one any verifier can recompute ('sha256' or 'swarm'). + */ +export function addressingName(store) { + if (store && (store.addressing === 'sha256' || store.addressing === 'swarm')) return store.addressing; + throw new TypeError("cannot determine this bytes store's addressing scheme; pass {addressing: 'sha256' | 'swarm'}"); +} + +// -- key proofs --------------------------------------------------------------- + +/** + * Check a key proof against `root` (the reference the verifier trusts; null + * for an empty store). Resolves to the proven record, or ABSENT for an + * absence proof; rejects with ProofError on any mismatch. + */ +export async function verifyProof(proof, root = null) { + envelope(proof, PROOF_FORMAT); + if ((proof.root ?? null) !== root) throw new ProofError(`proof is about root ${JSON.stringify(proof.root)}, not ${JSON.stringify(root)}`); + const refOf = addressing(proof.addressing); + const { key } = proof; + if (typeof key !== 'string' || key === '' || !isWellFormed(key)) throw new ProofError('proof carries no key'); + const nodes = blobs(proof.nodes, 'node'); + + let expected = root; + let remaining = utf8Hex(key); + let verdict = null; // [found, value ref] once the walk concludes + for (let i = 0; i < nodes.length; i++) { + if (verdict) throw new ProofError(`node ${i} continues past the walk's conclusion`); + if ((await refOf(nodes[i])) !== expected) throw new ProofError(`node ${i} does not hash to the expected reference`); + const node = decodeNode(nodes[i], `node ${i}`); + if (!remaining.startsWith(node.prefix)) { + verdict = [false, null]; // diverges inside the prefix + } else { + remaining = remaining.slice(node.prefix.length); + if (remaining === '') { + verdict = [node.valueRef !== null, node.valueRef]; + } else { + const child = node.children.get(remaining.slice(0, 2)); + if (child === undefined) { + verdict = [false, null]; // nowhere to descend + } else { + expected = child; + remaining = remaining.slice(2); + } + } + } + } + if (!verdict) { + if (root === null) verdict = [false, null]; // the empty store holds nothing + else throw new ProofError('proof ends before the walk does'); + } + + const [found, valueRef] = verdict; + if (found !== Boolean(proof.present)) throw new ProofError("the proof's claim contradicts its own path"); + if (!found) { + if (proof.value !== null && proof.value !== undefined) throw new ProofError('absence proof carries a value'); + return ABSENT; + } + const value = blob(proof.value, 'value'); + if ((await refOf(value)) !== valueRef) throw new ProofError("value bytes do not hash to the trie's value reference"); + return decodeValue(value); +} + +// -- walks shared by prefix and extension proofs ------------------------------ + +/** Can a subtree whose keys start with `keyPrefix` hold a key starting with `prefix`? */ +function meets(keyPrefix, prefix) { + return keyPrefix.startsWith(prefix) || prefix.startsWith(keyPrefix); +} + +/** + * [key, value ref, null] for every value under `prefix` in the subtree at + * `node`, in key order; children that cannot hold the prefix are not loaded. + */ +export async function* itemsUnder(load, node, acc, prefix) { + const stack = [[node, acc]]; + while (stack.length) { + const [n, a] = stack.pop(); + const full = a + n.prefix; + if (!meets(full, prefix)) continue; + if (n.valueRef !== null && full.startsWith(prefix)) yield [full, n.valueRef, null]; + for (const byte of [...n.children.keys()].sort().reverse()) { + const keyPrefix = full + byte; + if (meets(keyPrefix, prefix)) stack.push([await load(n.children.get(byte)), keyPrefix]); + } + } +} + +/** + * [key, a value ref, b value ref | null] for every record of `a`'s subtree + * under `prefix` that `b`'s subtree does not hold with the same value: the + * three shapes two canonical tries can differ in, one way only. + */ +async function* extensionFaults(load, a, b, acc, prefix) { + if (a === null) return; + if (!meets(acc + a.prefix, prefix)) return; + if (b === null) { + yield* itemsUnder(load, a, acc, prefix); + return; + } + const pa = a.prefix; + const pb = b.prefix; + if (pa === pb) { + const ka = acc + pa; + if (a.valueRef !== null && ka.startsWith(prefix) && a.valueRef !== b.valueRef) yield [ka, a.valueRef, b.valueRef]; + for (const byte of [...a.children.keys()].sort()) { + const ca = a.children.get(byte); + const cb = b.children.get(byte) ?? null; + const keyPrefix = ka + byte; + if (ca === cb || !meets(keyPrefix, prefix)) continue; // shared subtree, or outside the prefix + const na = await load(ca); + yield* extensionFaults(load, na, cb !== null ? await load(cb) : null, keyPrefix, prefix); + } + return; + } + const common = commonPrefix(pa, pb); + if (common.length < pa.length && common.length < pb.length) { + yield* itemsUnder(load, a, acc, prefix); // disjoint key ranges + } else if (common.length === pa.length) { + // a's edge is a proper prefix of b's: b lives under one of a's branches + const ka = acc + pa; + const bb = pb.slice(pa.length, pa.length + 2); + const bSplit = new TrieNode(pb.slice(pa.length + 2), b.valueRef, b.children); + if (a.valueRef !== null && ka.startsWith(prefix)) yield [ka, a.valueRef, null]; + for (const byte of [...a.children.keys()].sort()) { + const keyPrefix = ka + byte; + if (!meets(keyPrefix, prefix)) continue; + const child = await load(a.children.get(byte)); + if (byte === bb) yield* extensionFaults(load, child, bSplit, keyPrefix, prefix); + else yield* itemsUnder(load, child, keyPrefix, prefix); + } + } else { + // b's edge is a proper prefix of a's: a lives under one of b's branches + const kb = acc + pb; + const ab = pa.slice(pb.length, pb.length + 2); + const aSplit = new TrieNode(pa.slice(pb.length + 2), a.valueRef, a.children); + const cb = b.children.get(ab) ?? null; + yield* extensionFaults(load, aSplit, cb !== null ? await load(cb) : null, kb + ab, prefix); + } +} + +/** The records `base` held under `prefix` (hex) that `root` lacks or changed. */ +export async function* extensionWalk(load, base, root, prefix) { + if (base === null || base === root) return; // nothing held before, or the same state + const a = await load(base); + yield* extensionFaults(load, a, root !== null ? await load(root) : null, '', prefix); +} + +/** Prefixes as an extension proof names them: unique, in code point order, at least one. */ +export function checkPrefixes(prefixes) { + if (typeof prefixes === 'string') throw new TypeError('prefixes is a list of strings, not one string'); + const list = [...prefixes]; + if (!list.length || !list.every((p) => typeof p === 'string')) { + throw new TypeError("an extension proof names at least one string prefix ('' for every key)"); + } + return [...new Set(list)].sort(compareCodePoints); +} + +/** A loader over the blobs an envelope carries, indexed by their recomputed references. */ +async function carried(list, refOf, what) { + const byRef = new Map(); + for (const b of blobs(list, what)) byRef.set(await refOf(b), b); + return byRef; +} + +function loaderOver(nodes) { + return async (ref) => { + const bytes = nodes.get(ref); + if (!bytes) throw new ProofError(`the proof lacks node ${ref}`); + return decodeNode(bytes, `node ${ref}`); + }; +} + +// -- prefix proofs ------------------------------------------------------------ + +/** + * Check a prefix proof against `root` and resolve to a Map of every record + * under its prefix, proven complete; ProofError on any mismatch, including a + * node or value the walk needs and the proof lacks. + */ +export async function verifyPrefix(proof, root = null) { + envelope(proof, PREFIX_FORMAT); + if ((proof.root ?? null) !== root) throw new ProofError(`proof is about root ${JSON.stringify(proof.root)}, not ${JSON.stringify(root)}`); + const { prefix } = proof; + if (typeof prefix !== 'string' || !isWellFormed(prefix)) throw new ProofError('proof carries no prefix'); + const refOf = addressing(proof.addressing); + const nodes = await carried(proof.nodes, refOf, 'node'); + const values = await carried(proof.values, refOf, 'value'); + const load = loaderOver(nodes); + const out = new Map(); + if (root === null) return out; // the empty store holds nothing + for await (const [key, valueRef] of itemsUnder(load, await load(root), '', utf8Hex(prefix))) { + const value = values.get(valueRef); + if (!value) throw new ProofError(`the proof lacks the value of ${JSON.stringify(keyText(key))}`); + out.set(hexUtf8(key), decodeValue(value)); + } + return out; +} + +// -- extension proofs --------------------------------------------------------- + +/** + * Check that `root` extends `base` under the proof's prefixes (every record + * `base` held under them is held by `root` with the same value) and resolve + * to the prefixes proven. `base` and `root` are the references the verifier + * trusts (null: the empty store). + */ +export async function verifyExtension(proof, base = null, root = null) { + envelope(proof, EXTENSION_FORMAT); + if ((proof.base ?? null) !== base || (proof.root ?? null) !== root) { + throw new ProofError(`proof is about ${JSON.stringify(proof.base)} -> ${JSON.stringify(proof.root)}, not ${JSON.stringify(base)} -> ${JSON.stringify(root)}`); + } + const refOf = addressing(proof.addressing); + let prefixes; + try { + prefixes = checkPrefixes(proof.prefixes || []); + } catch (e) { + throw new ProofError(e.message); + } + if (!Array.isArray(proof.prefixes) || prefixes.length !== proof.prefixes.length || prefixes.some((p, i) => p !== proof.prefixes[i])) { + throw new ProofError("the proof's prefixes are not in canonical order"); + } + const load = loaderOver(await carried(proof.nodes, refOf, 'node')); + for (const prefix of prefixes) { + if (!isWellFormed(prefix)) throw new ProofError('a prefix contains a lone surrogate'); + for await (const [key, , b] of extensionWalk(load, base, root, utf8Hex(prefix))) { + throw new ProofError(`${JSON.stringify(keyText(key))} ${b === null ? 'is absent from' : 'changed in'} the later root`); + } + } + return prefixes; +} + +export { prefixHex }; diff --git a/js/src/recordstore.js b/js/src/recordstore.js new file mode 100644 index 0000000..5ac1500 --- /dev/null +++ b/js/src/recordstore.js @@ -0,0 +1,591 @@ +// RecordStore: a staged, versioned key -> record store over a bytes store, +// ported from Python's `RecordStore`. Same data model and wire format; an +// asynchronous, JavaScript-shaped API: +// +// - reads and commits return promises, iteration is `for await`; +// - `get` resolves to `undefined` for an absent key (records can be null); +// - `delete` resolves to `false` for an absent key instead of raising; +// - `has` is Python's `contains`. + +import { canonicalCopy, decodeValue, encodeValue } from './canonical.js'; +import { compareCodePoints, hexUtf8, isWellFormed, toHex, utf8Hex } from './bytes.js'; +import { DEFAULT_NODE_CACHE_SIZE, KeyMissing, Trie } from './trie.js'; +import { + EXTENSION_FORMAT, PREFIX_FORMAT, PROOF_FORMAT, addressingName, checkPrefixes, extensionWalk, itemsUnder, + verifyExtension, verifyPrefix, verifyProof, +} from './proofs.js'; + +/** A side of a diff or merge where the key is absent. */ +export const ABSENT = Symbol.for('recordstore.ABSENT'); +/** What a merge resolver returns to drop a key. */ +export const DELETE = Symbol.for('recordstore.DELETE'); + +const TOMBSTONE = Symbol('tombstone'); + +/** Both sides of a merge changed the same keys to different values. */ +export class MergeConflict extends Error { + constructor(conflicts) { + const keys = [...conflicts]; + const shown = keys.slice().sort().slice(0, 5).join(', ') + (keys.length > 5 ? ', ...' : ''); + super(`unresolved merge conflict on ${keys.length} key(s): ${shown}`); + this.name = 'MergeConflict'; + this.conflicts = keys; + } +} + +/** + * The key exists under the committed root, but a blob on its path or its + * value cannot be read right now (missing from the store, network failure). + * Never reported as absence. + */ +export class RecordUnavailable extends Error { + constructor(message, cause) { + super(message, { cause }); + this.name = 'RecordUnavailable'; + } +} + +/** UTF-8 hex of a key, after checking it is a non-empty well-formed string. */ +function keyHex(key) { + if (typeof key !== 'string' || key === '') throw new TypeError('key must be a non-empty string'); + if (!isWellFormed(key)) throw new TypeError('key contains a lone surrogate (not encodable as UTF-8)'); + return utf8Hex(key); +} + +function prefixHex(prefix) { + if (typeof prefix !== 'string') throw new TypeError("prefix is a string ('' for every key)"); + if (!isWellFormed(prefix)) throw new TypeError('prefix contains a lone surrogate'); + return utf8Hex(prefix); +} + +async function fetchBlobs(blobs, refs) { + if (!refs.length) return new Map(); + if (typeof blobs.getMany === 'function') return blobs.getMany(refs); + const out = new Map(); + for (const r of refs) out.set(r, await blobs.get(r)); + return out; +} + +async function putBlobs(blobs, datas) { + if (!datas.length) return []; + if (typeof blobs.putMany === 'function') return blobs.putMany(datas); + const refs = []; + for (const d of datas) refs.push(await blobs.put(d)); + return refs; +} + +/** Values are fetched in windows of this many while iterating. */ +const WINDOW = 256; + +export class RecordStore { + /** + * @param {object} blobs a bytes store + * @param {{root?: string | null, pointer?: object | null, readonly?: boolean, + * nodeCacheSize?: number}} [options] + * `root`: the committed state to start from (null: empty). With a + * `pointer` and no root, use `RecordStore.open`, which reads the + * pointer first. + */ + constructor(blobs, { root = null, pointer = null, readonly = false, nodeCacheSize = DEFAULT_NODE_CACHE_SIZE } = {}) { + this._blobs = blobs; + this._trie = new Trie(blobs, nodeCacheSize); + this._root = root; + this._pointer = pointer; + this._readonly = readonly; + /** @type {Map} key -> staged value or TOMBSTONE */ + this._staged = new Map(); + } + + /** A store at its pointer's current root. */ + static async open(blobs, { pointer = null, ...options } = {}) { + const root = pointer ? await pointer.get() : null; + return new RecordStore(blobs, { ...options, root, pointer }); + } + + /** A read-only snapshot of any root. */ + static at(root, blobs) { + return new RecordStore(blobs, { root, readonly: true }); + } + + /** Root of the last committed state (staged changes not included). */ + get root() { + return this._root; + } + + /** The underlying bytes store. */ + get blobs() { + return this._blobs; + } + + /** Number of staged, uncommitted changes. */ + get stagedCount() { + return this._staged.size; + } + + // -- reading -------------------------------------------------------------- + + /** The record under `key` (a fresh copy), or `undefined` if absent. */ + async get(key) { + const kh = keyHex(key); + if (this._staged.has(key)) { + const staged = this._staged.get(key); + return staged === TOMBSTONE ? undefined : canonicalCopy(staged); + } + let vref; + try { + vref = await this._trie.get(this._root, kh); + } catch (e) { + throw new RecordUnavailable(`record ${JSON.stringify(key)}: a trie node on its path is unreachable: ${e.message}`, e); + } + if (vref === null) return undefined; + let blob; + try { + blob = await this._blobs.get(vref); + } catch (e) { + throw new RecordUnavailable(`record ${JSON.stringify(key)} exists (value ${vref.slice(0, 16)}...) but its bytes are unreachable: ${e.message}`, e); + } + return decodeValue(blob); + } + + /** Is there a record under `key`? */ + async has(key) { + return (await this.get(key)) !== undefined; + } + + /** + * The records under several keys, read together (about one round trip per + * trie level, and the values in one batch). Absent keys are left out. + * @returns {Promise>} + */ + async getMany(keys) { + const out = new Map(); + const walk = new Map(); // key hex -> key + for (const key of keys) { + const kh = keyHex(key); + if (this._staged.has(key)) { + const staged = this._staged.get(key); + if (staged !== TOMBSTONE) out.set(key, canonicalCopy(staged)); + } else { + walk.set(kh, key); + } + } + let wanted; + let blobs; + try { + const vrefs = await this._trie.getMany(this._root, [...walk.keys()]); + wanted = new Map(); + for (const [kh, vref] of vrefs) if (vref !== null) wanted.set(walk.get(kh), vref); + blobs = await fetchBlobs(this._blobs, [...new Set(wanted.values())].sort()); + } catch (e) { + throw new RecordUnavailable(`${walk.size} records: part of their trie or values is unreachable: ${e.message}`, e); + } + for (const [key, vref] of wanted) out.set(key, decodeValue(blobs.get(vref))); + return out; + } + + /** + * Committed and staged entries under `prefix`, in sorted key order, as + * [key, value ref | null, staged value | undefined]; tombstones dropped. + */ + async *_merged(prefix) { + const ph = prefixHex(prefix); + const staged = [...this._staged.keys()] + .map((k) => [utf8Hex(k), k]) + .filter(([kh]) => kh.startsWith(ph)) + .sort((x, y) => (x[0] < y[0] ? -1 : x[0] > y[0] ? 1 : 0)); + let si = 0; + for await (const [kh, vref] of this._trie.items(this._root, ph)) { + while (si < staged.length && staged[si][0] < kh) { + const sk = staged[si++][1]; + const v = this._staged.get(sk); + if (v !== TOMBSTONE) yield [sk, null, v]; + } + if (si < staged.length && staged[si][0] === kh) { + const sk = staged[si++][1]; + const v = this._staged.get(sk); + if (v !== TOMBSTONE) yield [sk, null, v]; + } else { + yield [hexUtf8(kh), vref, undefined]; + } + } + while (si < staged.length) { + const sk = staged[si++][1]; + const v = this._staged.get(sk); + if (v !== TOMBSTONE) yield [sk, null, v]; + } + } + + /** Sorted keys under `prefix` (code point order, as Python), staged changes included. */ + async *keys(prefix = '') { + for await (const [key] of this._merged(prefix)) yield key; + } + + /** Sorted [key, value] under `prefix`, staged changes included; values fetched a window at a time. */ + async *items(prefix = '') { + let buf = []; + let refs = []; + const flush = async function* (store) { + const blobs = await fetchBlobs(store._blobs, refs); + for (const [key, vref, staged] of buf) { + yield [key, vref === null ? canonicalCopy(staged) : decodeValue(blobs.get(vref))]; + } + }; + for await (const entry of this._merged(prefix)) { + buf.push(entry); + if (entry[1] !== null) refs.push(entry[1]); + if (refs.length >= WINDOW) { + yield* flush(this); + buf = []; + refs = []; + } + } + if (buf.length) yield* flush(this); + } + + /** + * [key, mine, theirs] for every key whose value differs between this + * store's committed root and `otherRoot`; a side without the key is + * `ABSENT`. Cost follows the difference, not the dataset. Keys arrive in + * no particular order. + */ + async *diff(otherRoot) { + let buf = []; + const flush = async function* (store) { + const refs = [...new Set(buf.flatMap(([, a, b]) => [a, b]).filter((r) => r !== null))]; + const blobs = await fetchBlobs(store._blobs, refs); + for (const [kh, mine, theirs] of buf) { + yield [hexUtf8(kh), + mine === null ? ABSENT : decodeValue(blobs.get(mine)), + theirs === null ? ABSENT : decodeValue(blobs.get(theirs))]; + } + }; + for await (const entry of this._trie.diff(this._root, otherRoot)) { + buf.push(entry); + if (buf.length >= WINDOW) { + yield* flush(this); + buf = []; + } + } + if (buf.length) yield* flush(this); + } + + /** ['node', ref] / ['value', ref] for every blob the keys under `prefix` need in the committed root. */ + refsUnder(prefix = '') { + return this._trie.refsUnder(this._root, prefixHex(prefix)); + } + + // -- proofs --------------------------------------------------------------- + + /** + * A proof that `key` holds its record in the committed root, or that it is + * absent: the raw nodes on the key's one possible path, and the value. + * `verifyProof(proof, root)` checks it with no store. Refused for a key + * with staged changes (proofs are about committed roots). `addressing` + * defaults to the bytes store's. Every proof is checked before it is + * returned. + */ + async prove(key, { addressing } = {}) { + const kh = keyHex(key); + if (this._staged.has(key)) { + throw new Error(`${JSON.stringify(key)} has staged, uncommitted changes; proofs are statements about a committed root, commit() first`); + } + const name = addressing ?? addressingName(this._blobs); + const nodes = []; + let present = false; + let value = null; + let ref = this._root; + let remaining = kh; + while (ref !== null) { + const blob = await this._blobs.get(ref); // the exact bytes behind the reference + nodes.push(toHex(blob)); + const node = Trie.decode(blob); + if (!remaining.startsWith(node.prefix)) break; + remaining = remaining.slice(node.prefix.length); + if (remaining === '') { + if (node.valueRef !== null) { + present = true; + value = toHex(await this._blobs.get(node.valueRef)); + } + break; + } + ref = node.children.get(remaining.slice(0, 2)) ?? null; + remaining = remaining.slice(2); + } + const proof = { format: PROOF_FORMAT, version: 1, addressing: name, root: this._root, key, present, nodes, value }; + await verifyProof(proof, this._root); // never hand out a broken proof + return proof; + } + + /** + * A proof of every record under `prefix` ('' for the whole store) in the + * committed root, and of no others. `verifyPrefix(proof, root)` returns + * them all. Its size is the size of what is under the prefix. + */ + async provePrefix(prefix = '', { addressing } = {}) { + if (typeof prefix !== 'string') throw new TypeError("prefix is a string ('' for every key)"); + const staged = [...this._staged.keys()].filter((k) => k.startsWith(prefix)).sort(compareCodePoints); + if (staged.length) throw new Error(`${JSON.stringify(staged[0])} has staged, uncommitted changes under the prefix; commit() first`); + const name = addressing ?? addressingName(this._blobs); + const nodes = new Map(); + const values = new Map(); + const load = async (ref) => { + const blob = await this._blobs.get(ref); + nodes.set(ref, toHex(blob)); + return Trie.decode(blob); + }; + if (this._root !== null) { + for await (const [, valueRef] of itemsUnder(load, await load(this._root), '', prefixHex(prefix))) { + values.set(valueRef, toHex(await this._blobs.get(valueRef))); + } + } + const proof = { + format: PREFIX_FORMAT, version: 1, addressing: name, root: this._root, prefix, + nodes: [...nodes.values()].sort(), values: [...values.values()].sort(), + }; + await verifyPrefix(proof, this._root); + return proof; + } + + /** + * Does the committed root hold, under each of `prefixes`, every record + * `base` held, unchanged (additions allowed)? `base`'s nodes must be in + * this store's bytes store. + */ + async extends(base, prefixes = ['']) { + const load = (ref) => this._trie.load(ref); + for (const p of checkPrefixes(prefixes)) { + const faults = extensionWalk(load, base ?? null, this._root, prefixHex(p)); + if (!(await faults.next()).done) return false; + } + return true; + } + + /** + * A proof that the committed root extends `base` under `prefixes`: no + * record `base` held under them was removed or changed. Its nodes are + * those of the two roots along what changed under the prefixes. Throws + * when the root does not extend `base` (naming the key). + */ + async proveExtension(base, prefixes = [''], { addressing } = {}) { + const list = checkPrefixes(prefixes); + const staged = [...this._staged.keys()].filter((k) => list.some((p) => k.startsWith(p))); + if (staged.length) throw new Error(`${JSON.stringify(staged[0])} has staged, uncommitted changes under the prefixes; commit() first`); + const name = addressing ?? addressingName(this._blobs); + const blobs = new Map(); + const load = async (ref) => { + const blob = await this._blobs.get(ref); + blobs.set(ref, toHex(blob)); + return Trie.decode(blob); + }; + base = base ?? null; + for (const prefix of list) { + for await (const [key, , b] of extensionWalk(load, base, this._root, prefixHex(prefix))) { + throw new Error(`${JSON.stringify(this._root)} does not extend ${JSON.stringify(base)} under ${JSON.stringify(prefix)}: ` + + `${JSON.stringify(hexUtf8(key))} is ${b === null ? 'absent from' : 'changed in'} it`); + } + } + const proof = { + format: EXTENSION_FORMAT, version: 1, addressing: name, base, root: this._root, + prefixes: list, nodes: [...blobs.values()].sort(), + }; + await verifyExtension(proof, base, this._root); + return proof; + } + + // -- writing -------------------------------------------------------------- + + /** Stage `value` under `key` (validated and copied now, written on commit). */ + put(key, value) { + if (this._readonly) throw new TypeError('read-only snapshot'); + keyHex(key); + this._staged.set(key, canonicalCopy(value)); + } + + /** Stage the removal of `key`; resolves to false (staging nothing) if there is no such record. */ + async delete(key) { + if (this._readonly) throw new TypeError('read-only snapshot'); + const kh = keyHex(key); + if (this._staged.has(key)) { + if (this._staged.get(key) === TOMBSTONE) return false; // already deleted in this batch + } else if ((await this._trie.get(this._root, kh)) === null) { + return false; + } + this._staged.set(key, TOMBSTONE); + return true; + } + + /** Apply the staged changes on top of `base`; return the new root. */ + async _buildRoot(base) { + const order = [...this._staged.keys()].map((k) => [utf8Hex(k), k]) + .sort((x, y) => (x[0] < y[0] ? -1 : x[0] > y[0] ? 1 : 0)); + const writes = order.filter(([, k]) => this._staged.get(k) !== TOMBSTONE); + const refs = await putBlobs(this._blobs, writes.map(([, k]) => encodeValue(this._staged.get(k)))); + const vref = new Map(writes.map(([kh], i) => [kh, refs[i]])); + try { + let root = base; + for (const [kh, k] of order) { + if (this._staged.get(k) === TOMBSTONE) { + try { + root = await this._trie.delete(root, kh); + } catch (e) { + if (!(e instanceof KeyMissing)) throw e; // deleted a key that never existed + } + } else { + root = await this._trie.insert(root, kh, vref.get(kh)); + } + } + return await this._trie.flush(root); + } finally { + this._trie.reset(); + } + } + + /** + * Write the staged changes and return the new root; then move the pointer, + * if there is one. With `reconcile`, a pointer that moved since this store + * read it is three-way merged into instead of overwritten (`resolver` + * settles conflicts), retrying up to `retries` times. + */ + async commit({ reconcile = false, resolver = null, retries = 5 } = {}) { + if (this._readonly) throw new TypeError('read-only snapshot'); + const base = this._root; + let root = await this._buildRoot(base); + if (this._pointer) { + if (reconcile) root = await this._reconcile(base, root, resolver, retries); + else await this._pointer.set(root); + } + this._staged.clear(); + this._root = root; + return root; + } + + async _reconcile(base, root, resolver, retries) { + const pointer = this._pointer; + const cas = typeof pointer.compareAndSet === 'function' ? pointer.compareAndSet.bind(pointer) : null; + let expected = base; + for (let i = 0; i < Math.max(1, retries); i++) { + const current = await pointer.get(); + if (current === expected) { + if (!cas) { + await pointer.set(root); // best effort: no compare-and-set + return root; + } + if (await cas(expected, root)) return root; + continue; // lost the race: read again + } + root = await RecordStore.merge(this._blobs, expected, root, current, resolver); + expected = current; + } + throw new Error(`commit could not reconcile the pointer after ${retries} tries`); + } + + // -- merge ---------------------------------------------------------------- + + /** + * Three-way merge of two roots that diverged from `base`; resolves to the + * merged root. A change on one side is taken, the same change on both + * taken once, different changes to one key are a conflict, settled by + * `resolver(key, base, ours, theirs)` (values or ABSENT; return a value or + * DELETE; may be async) or else raised as MergeConflict. + */ + static async merge(blobs, base, ours, theirs, resolver = null) { + if (ours === theirs) return ours; + if (ours === base) return theirs; + if (theirs === base) return ours; + + const trie = new Trie(blobs); + const ourDiff = new Map(); + for await (const [k, bv, sv] of trie.diff(base, ours)) ourDiff.set(k, [bv, sv]); + const theirDiff = new Map(); + for await (const [k, bv, sv] of trie.diff(base, theirs)) theirDiff.set(k, [bv, sv]); + + const changes = new Map(); // key hex -> value ref | TOMBSTONE + const conflicts = []; + for (const k of new Set([...ourDiff.keys(), ...theirDiff.keys()])) { + let bv; + let merged; + if (ourDiff.has(k) && theirDiff.has(k)) { + [bv, merged] = ourDiff.get(k); + if (merged !== theirDiff.get(k)[1]) { + conflicts.push(k); + continue; + } + } else if (ourDiff.has(k)) { + [bv, merged] = ourDiff.get(k); + } else { + [bv, merged] = theirDiff.get(k); + } + if (merged !== bv) changes.set(k, merged !== null ? merged : TOMBSTONE); + } + + if (conflicts.length && !resolver) throw new MergeConflict(conflicts.map(hexUtf8)); + if (conflicts.length) { + const resolved = await RecordStore._resolve(blobs, conflicts.sort(), ourDiff, theirDiff, resolver); + for (const [k, c] of resolved) changes.set(k, c); + } + + try { + let root = base; + for (const k of [...changes.keys()].sort()) { + const c = changes.get(k); + if (c === TOMBSTONE) { + try { + root = await trie.delete(root, k); + } catch (e) { + if (!(e instanceof KeyMissing)) throw e; + } + } else { + root = await trie.insert(root, k, c); + } + } + return await trie.flush(root); + } finally { + trie.reset(); + } + } + + static async _resolve(blobs, conflicts, ourDiff, theirDiff, resolver) { + const trio = new Map(conflicts.map((k) => [k, [ourDiff.get(k)[0], ourDiff.get(k)[1], theirDiff.get(k)[1]]])); + const refs = [...new Set([...trio.values()].flat().filter((r) => r !== null))]; + const values = await fetchBlobs(blobs, refs); + const decode = (r) => (r === null ? ABSENT : decodeValue(values.get(r))); + const kept = new Map(); + for (const k of conflicts) { + const [bv, ov, tv] = trio.get(k); + kept.set(k, await resolver(hexUtf8(k), decode(bv), decode(ov), decode(tv))); + } + const writes = conflicts.filter((k) => kept.get(k) !== DELETE); + const newRefs = await putBlobs(blobs, writes.map((k) => encodeValue(kept.get(k)))); + const refOf = new Map(writes.map((k, i) => [k, newRefs[i]])); + const changes = new Map(); + for (const k of conflicts) { + const merged = refOf.get(k) ?? null; // null: the resolver deleted it + if (merged !== trio.get(k)[0]) changes.set(k, merged !== null ? merged : TOMBSTONE); + } + return changes; + } +} + +/** + * A "latest root" held in memory, with compare-and-set: what several + * writers in one process can share, and the test double for a feed. + */ +export class MemoryPointer { + constructor(root = null) { + this.root = root; + } + + async get() { + return this.root; + } + + async set(root) { + this.root = root; + } + + async compareAndSet(expected, root) { + if (this.root !== expected) return false; + this.root = root; + return true; + } +} diff --git a/js/src/stores.js b/js/src/stores.js new file mode 100644 index 0000000..019784c --- /dev/null +++ b/js/src/stores.js @@ -0,0 +1,230 @@ +// Bytes stores: where blobs live. The interface, as in Python: +// +// put(data: Uint8Array) -> Promise +// get(reference) -> Promise +// +// and optionally `putMany(datas)` / `getMany(refs)` (a Map) for batched or +// parallel I/O, which recordstore uses when present. + +import { redundancyFor, resolveAddressing, splitData, swarmRef } from './address.js'; +import { fromBase64 } from './bytes.js'; + +/** A reference the store does not hold. */ +export class BlobNotFoundError extends Error { + constructor(ref) { + super(`reference not found: ${ref}`); + this.name = 'BlobNotFoundError'; + this.ref = ref; + } +} + +/** Bytes that do not hash to the reference they were fetched or stored under. */ +export class VerificationError extends Error { + constructor(message) { + super(message); + this.name = 'VerificationError'; + } +} + +/** Run `fn` over `items` with at most `limit` in flight; results in order. */ +async function pool(items, limit, fn) { + const out = new Array(items.length); + let next = 0; + const worker = async () => { + while (next < items.length) { + const i = next++; + out[i] = await fn(items[i], i); + } + }; + await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker)); + return out; +} + +/** + * In-memory content-addressed store, the test double for Swarm. Its + * references are SHA-256 by default, as Python's MemoryBytesStore; with + * `addressing: 'swarm'` they are the references Swarm would give. + */ +export class MemoryBytesStore { + /** @param {{addressing?: 'sha256' | 'swarm' | ((data: Uint8Array) => string | Promise)}} [options] */ + constructor({ addressing = 'sha256' } = {}) { + this.addressing = addressing; + this._address = resolveAddressing(addressing); + /** @type {Map} */ + this.blobs = new Map(); + } + + async put(data) { + const ref = await this._address(data); + this.blobs.set(ref, Uint8Array.from(data)); + return ref; + } + + async get(ref) { + const data = this.blobs.get(ref); + if (data === undefined) throw new BlobNotFoundError(ref); + return Uint8Array.from(data); + } + + async getMany(refs) { + const out = new Map(); + for (const ref of refs) out.set(ref, await this.get(ref)); + return out; + } + + async putMany(datas) { + const refs = []; + for (const data of datas) refs.push(await this.put(data)); + return refs; + } + + get size() { + return this.blobs.size; + } +} + +/** + * Blobs on a Bee node (or any server with Bee's `/bytes` API, such as Ant), + * the counterpart of Python's BeeBytesStore. Reads need no postage batch; + * writes do. Every blob read is checked against its reference by recomputing + * the Swarm address, and every write against the address the node returns. + * That holds for blobs uploaded plainly, as recordstore uploads them (Python + * and JS both); pass `verify: false` for a store written with erasure coding. + */ +export class BeeBytesStore { + /** + * @param {string} url the node's API, e.g. http://localhost:1633 + * @param {{batch?: string, verify?: boolean, deferred?: boolean, + * concurrency?: number, fetch?: typeof fetch}} [options] + */ + constructor(url, { batch = null, verify = true, deferred = true, concurrency = 8, fetch: fetchFn } = {}) { + this.url = url.replace(/\/+$/, ''); + this.batch = batch; + this.verify = verify; + this.deferred = deferred; + this.concurrency = concurrency; + this._fetch = fetchFn || globalThis.fetch.bind(globalThis); + this.addressing = 'swarm'; + } + + async get(ref) { + const res = await this._fetch(`${this.url}/bytes/${ref}`); + if (res.status === 404) throw new BlobNotFoundError(ref); + if (!res.ok) throw new Error(`GET /bytes/${ref}: HTTP ${res.status}`); + const data = new Uint8Array(await res.arrayBuffer()); + if (this.verify && swarmRef(data) !== ref) { + throw new VerificationError(`the node returned bytes that do not hash to ${ref}`); + } + return data; + } + + async put(data) { + if (!this.batch) throw new Error('writing to Bee needs a postage batch (the `batch` option)'); + const res = await this._fetch(`${this.url}/bytes`, { + method: 'POST', + headers: { + 'content-type': 'application/octet-stream', + 'swarm-postage-batch-id': this.batch, + 'swarm-redundancy-level': String(redundancyFor(data.length)), + 'swarm-deferred-upload': String(this.deferred), + }, + body: data, + }); + if (!res.ok) throw new Error(`POST /bytes: HTTP ${res.status} ${await res.text().catch(() => '')}`); + const { reference } = await res.json(); + if (this.verify && reference !== swarmRef(data)) { + throw new VerificationError(`the node stored the blob under ${reference}, not its plain Swarm address`); + } + return reference; + } + + async getMany(refs) { + const datas = await pool(refs, this.concurrency, (ref) => this.get(ref)); + return new Map(refs.map((ref, i) => [ref, datas[i]])); + } + + async putMany(datas) { + return pool(datas, this.concurrency, (data) => this.put(data)); + } +} + +/** + * Blobs through a browser's `window.swarm` provider (Freedom Browser), where + * a page cannot reach a node's HTTP API. Reads use `swarm_readData` (the node + * joins the blob) and are checked against the reference here as well. Writes + * split the blob into chunks as Bee does and publish each with + * `swarm_publishChunk`, so references match Bee's and Python's; they need + * the page to be connected (`swarm_requestAccess`), which the first write + * asks for. Untested in Freedom so far. + */ +export class SwarmProviderBytesStore { + /** @param {{request: (args: {method: string, params?: object}) => Promise}} [provider] */ + constructor(provider = globalThis.swarm, { verify = true, concurrency = 4 } = {}) { + if (!provider || typeof provider.request !== 'function') { + throw new TypeError('no window.swarm provider (open the page in Freedom Browser)'); + } + this.provider = provider; + this.verify = verify; + this.concurrency = concurrency; + this.addressing = 'swarm'; + this._access = null; + } + + async get(ref) { + let result; + try { + result = await this.provider.request({ method: 'swarm_readData', params: { reference: ref } }); + } catch (e) { + if (e && e.data && e.data.reason === 'chunk_not_found') throw new BlobNotFoundError(ref); + throw e; + } + const data = fromBase64(result.data); + if (this.verify && swarmRef(data) !== ref) { + throw new VerificationError(`the provider returned bytes that do not hash to ${ref}`); + } + return data; + } + + async put(data) { + if (data.length === 0) throw new RangeError('the provider publishes chunks of 1 to 4096 bytes; an empty blob cannot be written'); + // A refused or failed request is not remembered: the next write asks again. + this._access ??= this.provider.request({ method: 'swarm_requestAccess' }) + .catch((e) => { this._access = null; throw e; }); + await this._access; + const { root, chunks } = splitData(data); + let rest = chunks; + if (!this._primed) { + // The store's first chunk goes out alone. Freedom asks before each + // publish until the user ticks "Always allow this site"; requests sent + // in parallel would each be waiting on a prompt before that answer + // lands (five prompts for one commit in the first trial). The rest + // wait for this one, then run in parallel. + this._primed = this._publishChunk(chunks[0]).catch((e) => { this._primed = null; throw e; }); + await this._primed; + rest = chunks.slice(1); + } else { + await this._primed; + } + await pool(rest, this.concurrency, (chunk) => this._publishChunk(chunk)); + return root; + } + + async _publishChunk({ address, payload, span }) { + const { reference } = await this.provider.request({ + method: 'swarm_publishChunk', + params: span === payload.length ? { data: payload } : { data: payload, span }, + }); + if (reference !== address) { + throw new VerificationError(`the provider published a chunk as ${reference}, expected ${address}`); + } + } + + async getMany(refs) { + const datas = await pool(refs, this.concurrency, (ref) => this.get(ref)); + return new Map(refs.map((ref, i) => [ref, datas[i]])); + } + + async putMany(datas) { + return pool(datas, this.concurrency, (data) => this.put(data)); + } +} diff --git a/js/src/swarm.js b/js/src/swarm.js new file mode 100644 index 0000000..04de78d --- /dev/null +++ b/js/src/swarm.js @@ -0,0 +1,26 @@ +// A whole store on Swarm in one call, as Python's `swarm_store`: blobs on a +// Bee node, the latest root in a Swarm feed. + +import { RecordStore } from './recordstore.js'; +import { BeeBytesStore } from './stores.js'; +import { SwarmFeedPointer } from './feeds.js'; + +/** + * A RecordStore that lives on Swarm, at its feed's current root. + * + * await swarmStore('my-notes', { signer: key, batch }) // publish your own + * await swarmStore('my-notes', { owner: address }) // follow someone else's + * + * Writing needs a postage batch of the node's (`batch`); unlike Python's + * `stamp="auto"`, none is picked for you. + */ +export async function swarmStore(topic, { + url = 'http://localhost:1633', batch = null, signer = null, owner = null, feedTtl = 15, concurrency = 32, +} = {}) { + if (!signer && !owner) { + throw new TypeError('swarmStore needs a signer (private key hex, to publish) or an owner (address hex, to follow)'); + } + const blobs = new BeeBytesStore(url, { batch, concurrency }); + const pointer = new SwarmFeedPointer(url, topic, { signer, owner, batch: signer ? batch : null, feedTtl }); + return RecordStore.open(blobs, { pointer }); +} diff --git a/js/src/trie.js b/js/src/trie.js new file mode 100644 index 0000000..2949211 --- /dev/null +++ b/js/src/trie.js @@ -0,0 +1,527 @@ +// The canonical persistent radix trie, ported from Python's `_Trie`. +// +// Each node is one blob, the canonical JSON of +// {"c": {: , ...}, "p": , +// "tn": 1, "v": } +// A child's prefix excludes the byte its parent branched on. Two invariants +// make the trie's shape a pure function of its contents (hence equal content +// => equal root): a node with no value and no children does not exist, and a +// node with no value and one child is merged into that child. +// +// Keys are lowercase hex of their UTF-8 bytes throughout (see bytes.js). +// Operations take a root and return a new root; nodes built during a commit +// are kept as `pending:N` placeholders and written by `flush`, bottom-up, +// one batch per level, only those reachable from the final root. + +import { canonicalBytes } from './canonical.js'; +import { fromUtf8 } from './bytes.js'; + +/** Default bound on the decoded-node cache (nodes are immutable and re-fetchable). */ +export const DEFAULT_NODE_CACHE_SIZE = 65536; + +/** Most nodes a walk loads ahead of where it is. */ +const WALK_LOOKAHEAD = 4096; + +export class TrieNode { + /** + * @param {string} prefix hex + * @param {string | null} valueRef + * @param {Map} children first byte (2 hex) -> child ref + */ + constructor(prefix, valueRef, children) { + this.prefix = prefix; + this.valueRef = valueRef; + this.children = children; + } +} + +/** Raised by `Trie.delete` for a key the trie does not hold. */ +export class KeyMissing extends Error { + constructor(key) { + super(`key not in trie: ${key}`); + this.name = 'KeyMissing'; + } +} + +/** Bounded LRU over decoded nodes (Map iteration order is insertion order). */ +class NodeCache { + constructor(maxsize) { + this.maxsize = Math.max(1, maxsize); + this.map = new Map(); + } + + get(ref) { + const node = this.map.get(ref); + if (node !== undefined) { + this.map.delete(ref); + this.map.set(ref, node); + } + return node; + } + + has(ref) { + return this.map.has(ref); + } + + set(ref, node) { + this.map.delete(ref); + this.map.set(ref, node); + while (this.map.size > this.maxsize) this.map.delete(this.map.keys().next().value); + } +} + +/** Longest common prefix of two hex strings, on a byte boundary. */ +export function commonPrefix(a, b) { + const n = Math.min(a.length, b.length); + let i = 0; + while (i < n && a[i] === b[i]) i++; + return a.slice(0, i - (i % 2)); +} + +/** A hex string's prefixes agree up to the shorter of the two. */ +function canMeet(full, prefix) { + const probe = Math.min(full.length, prefix.length); + return full.slice(0, probe) === prefix.slice(0, probe); +} + +const sortedBytes = (children) => [...children.keys()].sort(); + +export class Trie { + /** + * @param {{get: Function, put: Function, getMany?: Function, putMany?: Function}} blobs + * @param {number} [cacheSize] + */ + constructor(blobs, cacheSize = DEFAULT_NODE_CACHE_SIZE) { + this.blobs = blobs; + this.cache = new NodeCache(cacheSize); + /** @type {Map} */ + this.pending = new Map(); + this.pn = 0; + } + + // -- node io -------------------------------------------------------------- + + /** Decode a node blob. */ + static decode(bytes) { + const obj = JSON.parse(fromUtf8(bytes)); + if (obj === null || typeof obj !== 'object' || obj.tn !== 1) { + throw new Error('not a trie node or unsupported version'); + } + const children = new Map(); + for (const [k, ref] of Object.entries(obj.c)) { + const b = parseInt(k, 16); + if (!(b >= 0 && b <= 255)) throw new Error(`bad child key in trie node: ${k}`); + children.set(b.toString(16).padStart(2, '0'), ref); + } + return new TrieNode(obj.p.toLowerCase(), obj.v, children); + } + + /** The canonical bytes of a node. */ + static serialize(node) { + return canonicalBytes({ + tn: 1, + p: node.prefix, + v: node.valueRef, + c: Object.fromEntries(node.children), + }); + } + + /** At hand without a fetch: a placeholder of the commit being built, or cached. */ + has(ref) { + return this.pending.has(ref) || this.cache.has(ref); + } + + /** A node if at hand, `undefined` if not; a `null` side stays `null`. */ + peek(side) { + if (typeof side !== 'string') return side; + return this.pending.get(side) || this.cache.get(side); + } + + async load(ref) { + const pending = this.pending.get(ref); + if (pending) return pending; + let node = this.cache.get(ref); + if (!node) { + node = Trie.decode(await this.blobs.get(ref)); + this.cache.set(ref, node); + } + return node; + } + + /** Load several nodes, fetching the uncached ones in one batch. */ + async loadMany(refs) { + const out = new Map(); + const unique = [...new Set(refs)]; + for (const r of unique) { + const node = this.pending.get(r) || this.cache.get(r); + if (node) out.set(r, node); + } + const missing = unique.filter((r) => !out.has(r)); + if (missing.length) { + let blobs; + if (typeof this.blobs.getMany === 'function') { + blobs = await this.blobs.getMany(missing); + } else { + blobs = new Map(); + for (const r of missing) blobs.set(r, await this.blobs.get(r)); + } + for (const r of missing) { + const node = Trie.decode(blobs.get(r)); + this.cache.set(r, node); + out.set(r, node); // held here even if the LRU drops it + } + } + return out; + } + + /** Defer a node: a placeholder, resolved to its real reference by `flush`. */ + store(node) { + const pid = `pending:${this.pn++}`; + this.pending.set(pid, node); + return pid; + } + + /** + * Write the placeholders reachable from `root`, bottom-up, one batch per + * level, and return the real root reference. Orphaned intermediates are + * never written. + */ + async flush(root) { + if (root === null || !this.pending.has(root)) return root; + const reachable = new Set(); + const stack = [root]; + while (stack.length) { + const pid = stack.pop(); + if (reachable.has(pid)) continue; + reachable.add(pid); + for (const c of this.pending.get(pid).children.values()) if (this.pending.has(c)) stack.push(c); + } + const resolved = new Map(); + const remaining = new Set(reachable); + while (!resolved.has(root)) { + const ready = [...remaining].filter((pid) => + [...this.pending.get(pid).children.values()].every((c) => !this.pending.has(c) || resolved.has(c))); + if (!ready.length) throw new Error('trie flush stalled: no writable nodes'); + const batch = ready.map((pid) => { + const node = this.pending.get(pid); + const children = new Map([...node.children].map(([b, c]) => [b, resolved.get(c) ?? c])); + const real = new TrieNode(node.prefix, node.valueRef, children); + return [pid, real, Trie.serialize(real)]; + }); + const datas = batch.map((entry) => entry[2]); + let refs; + if (typeof this.blobs.putMany === 'function') { + refs = await this.blobs.putMany(datas); + } else { + refs = []; + for (const d of datas) refs.push(await this.blobs.put(d)); + } + batch.forEach(([pid, real], i) => { + resolved.set(pid, refs[i]); + this.cache.set(refs[i], real); + remaining.delete(pid); + }); + } + return resolved.get(root); + } + + /** Drop the placeholders of a finished (or failed) build. */ + reset() { + this.pending.clear(); + this.pn = 0; + } + + // -- reads ---------------------------------------------------------------- + + /** The value reference under `key` (hex), or null. */ + async get(root, key) { + while (root !== null) { + const node = await this.load(root); + if (!key.startsWith(node.prefix)) return null; + key = key.slice(node.prefix.length); + if (key === '') return node.valueRef; + root = node.children.get(key.slice(0, 2)) ?? null; + key = key.slice(2); + } + return null; + } + + /** + * `get` for many keys at once, level by level, so a network store pays + * about one round trip per trie level rather than one per node. + * @returns {Promise>} key hex -> value ref or null + */ + async getMany(root, keys) { + const found = new Map(); + const unique = [...new Set(keys)]; + if (root === null) { + for (const k of unique) found.set(k, null); + return found; + } + let walking = unique.map((k) => [k, k, root]); + while (walking.length) { + const nodes = await this.loadMany(walking.map((w) => w[2])); + const descending = []; + for (const [key, rest0, ref] of walking) { + const node = nodes.get(ref); + if (!rest0.startsWith(node.prefix)) { + found.set(key, null); + continue; + } + const rest = rest0.slice(node.prefix.length); + if (rest === '') { + found.set(key, node.valueRef); + continue; + } + const child = node.children.get(rest.slice(0, 2)); + if (child === undefined) found.set(key, null); + else descending.push([key, rest.slice(2), child]); + } + walking = descending; + } + return found; + } + + // -- writes (functional: a root in, a new root out) ------------------------- + + async insert(root, key, valueRef) { + if (root === null) return this.store(new TrieNode(key, valueRef, new Map())); + const node = await this.load(root); + const common = commonPrefix(node.prefix, key); + + if (common.length < node.prefix.length) { + // split: demote the existing node under the diverging byte + const demoted = new TrieNode(node.prefix.slice(common.length + 2), node.valueRef, new Map(node.children)); + const children = new Map([[node.prefix.slice(common.length, common.length + 2), this.store(demoted)]]); + const rest = key.slice(common.length); + if (rest === '') return this.store(new TrieNode(common, valueRef, children)); + children.set(rest.slice(0, 2), this.store(new TrieNode(rest.slice(2), valueRef, new Map()))); + return this.store(new TrieNode(common, null, children)); + } + + const rest = key.slice(node.prefix.length); + if (rest === '') return this.store(new TrieNode(node.prefix, valueRef, new Map(node.children))); + const children = new Map(node.children); + const childRef = children.get(rest.slice(0, 2)); + if (childRef === undefined) { + children.set(rest.slice(0, 2), this.store(new TrieNode(rest.slice(2), valueRef, new Map()))); + } else { + children.set(rest.slice(0, 2), await this.insert(childRef, rest.slice(2), valueRef)); + } + return this.store(new TrieNode(node.prefix, node.valueRef, children)); + } + + async delete(root, key) { + if (root === null) throw new KeyMissing(key); + const node = await this.load(root); + if (!key.startsWith(node.prefix)) throw new KeyMissing(key); + const rest = key.slice(node.prefix.length); + + if (rest === '') { + if (node.valueRef === null) throw new KeyMissing(key); + return this.canonicalize(node.prefix, null, new Map(node.children)); + } + + const childRef = node.children.get(rest.slice(0, 2)); + if (childRef === undefined) throw new KeyMissing(key); + const newChild = await this.delete(childRef, rest.slice(2)); + const children = new Map(node.children); + if (newChild === null) children.delete(rest.slice(0, 2)); + else children.set(rest.slice(0, 2), newChild); + return this.canonicalize(node.prefix, node.valueRef, children); + } + + /** Restore the canonical-form invariants after a removal. */ + async canonicalize(prefix, valueRef, children) { + if (valueRef === null && children.size === 0) return null; + if (valueRef === null && children.size === 1) { + const [[byte, childRef]] = children; + const child = await this.load(childRef); + return this.store(new TrieNode(prefix + byte + child.prefix, child.valueRef, new Map(child.children))); + } + return this.store(new TrieNode(prefix, valueRef, children)); + } + + // -- walks ------------------------------------------------------------------ + + /** + * Load what a depth-first walk will visit next, level by level: the + * unloaded refs on its stack (nearest first) and the children of those + * already loaded, skipping subtrees that cannot hold `prefix`. + */ + async prefetch(stack, prefix) { + const budget = Math.max(1, Math.min(WALK_LOOKAHEAD, Math.floor(this.cache.maxsize / 2))); + let frontier = [...stack].reverse(); + let fetched = 0; + let visited = 0; + while (frontier.length && fetched < budget && visited < 2 * budget) { + const missing = []; + for (const [ref] of frontier) if (!this.has(ref) && missing.length < budget - fetched) missing.push(ref); + if (missing.length) { + await this.loadMany(missing); + fetched += missing.length; + } + const next = []; + for (const [ref, acc] of frontier) { + const node = this.peek(ref); + if (!node) continue; // beyond this round's budget + visited++; + const full = acc + node.prefix; + if (!canMeet(full, prefix)) continue; + for (const byte of sortedBytes(node.children)) next.push([node.children.get(byte), full + byte]); + } + frontier = next; + } + } + + /** All [key hex, value ref] with key under `prefix` (hex), in sorted key order. */ + async *items(root, prefix = '') { + if (root === null) return; + const stack = [[root, '']]; + while (stack.length) { + if (!this.has(stack[stack.length - 1][0])) await this.prefetch(stack, prefix); + const [ref, acc] = stack.pop(); + const node = await this.load(ref); + const full = acc + node.prefix; + if (!canMeet(full, prefix)) continue; + if (node.valueRef !== null && full.startsWith(prefix)) yield [full, node.valueRef]; + for (const byte of sortedBytes(node.children).reverse()) stack.push([node.children.get(byte), full + byte]); + } + } + + /** + * ['node', ref] and ['value', ref] for every blob needed to read the keys + * under `prefix`: the nodes on the path and in the subtree, and the values. + */ + async *refsUnder(root, prefix = '') { + if (root === null) return; + const stack = [[root, '']]; + while (stack.length) { + if (!this.has(stack[stack.length - 1][0])) await this.prefetch(stack, prefix); + const [ref, acc] = stack.pop(); + const node = await this.load(ref); + const full = acc + node.prefix; + if (!canMeet(full, prefix)) continue; + yield ['node', ref]; + if (node.valueRef !== null && full.startsWith(prefix)) yield ['value', node.valueRef]; + for (const byte of sortedBytes(node.children)) stack.push([node.children.get(byte), full + byte]); + } + } + + // -- diff (structural, prunes shared subtrees) ------------------------------ + + /** + * [key hex, a value ref | null, b value ref | null] for every key where the + * two roots differ. Subtrees with equal refs are pruned, so the cost follows + * the difference, not the dataset. + */ + async *diff(aRoot, bRoot) { + if (aRoot === bRoot) return; + const stack = [[aRoot, bRoot, '']]; + while (stack.length) { + if (this.unloaded(stack[stack.length - 1])) await this.prefetchPairs(stack); + const [a, b, acc] = stack.pop(); + const { values, pairs } = Trie.diffStep(await this.asNode(a), await this.asNode(b), acc); + yield* values; + stack.push(...pairs); + } + } + + /** A pair's side is a ref, a split node (not itself stored), or null. */ + async asNode(side) { + return typeof side === 'string' ? this.load(side) : side; + } + + unloaded(pair) { + return [pair[0], pair[1]].some((x) => typeof x === 'string' && !this.has(x)); + } + + /** + * One pair of subtrees: the differing values at their tops, and the pairs + * still to compare. + */ + static diffStep(a, b, acc) { + const values = []; + const pairs = []; + if (a === null || b === null) { + const node = b === null ? a : b; + if (node === null) return { values, pairs }; + const full = acc + node.prefix; + if (node.valueRef !== null) values.push(b === null ? [full, node.valueRef, null] : [full, null, node.valueRef]); + for (const [byte, cref] of node.children) { + pairs.push(b === null ? [cref, null, full + byte] : [null, cref, full + byte]); + } + return { values, pairs }; + } + + const pa = a.prefix; + const pb = b.prefix; + if (pa === pb) { + const ka = acc + pa; + if (a.valueRef !== b.valueRef) values.push([ka, a.valueRef, b.valueRef]); + for (const byte of new Set([...a.children.keys(), ...b.children.keys()])) { + const ca = a.children.get(byte) ?? null; + const cb = b.children.get(byte) ?? null; + if (ca !== cb) pairs.push([ca, cb, ka + byte]); // equal refs: a shared subtree + } + return { values, pairs }; + } + + const common = commonPrefix(pa, pb); + if (common.length < pa.length && common.length < pb.length) { + // prefixes diverge: the two subtrees cover disjoint keys + pairs.push([a, null, acc]); + pairs.push([null, b, acc]); + } else if (common.length === pa.length) { + // a's key is a proper prefix of b's: b lives under one of a's branches + const ka = acc + pa; + const bb = pb.slice(pa.length, pa.length + 2); + const bSplit = new TrieNode(pb.slice(pa.length + 2), b.valueRef, b.children); + if (a.valueRef !== null) values.push([ka, a.valueRef, null]); + for (const [byte, cref] of a.children) pairs.push([cref, byte === bb ? bSplit : null, ka + byte]); + if (!a.children.has(bb)) pairs.push([null, bSplit, ka + bb]); + } else { + // b's key is a proper prefix of a's + const kb = acc + pb; + const ab = pa.slice(pb.length, pb.length + 2); + const aSplit = new TrieNode(pa.slice(pb.length + 2), a.valueRef, a.children); + if (b.valueRef !== null) values.push([kb, null, b.valueRef]); + for (const [byte, cref] of b.children) pairs.push([byte === ab ? aSplit : null, cref, kb + byte]); + if (!b.children.has(ab)) pairs.push([aSplit, null, kb + ab]); + } + return { values, pairs }; + } + + /** `prefetch` for a diff: load what the diff will visit, level by level. */ + async prefetchPairs(stack) { + const budget = Math.max(1, Math.min(WALK_LOOKAHEAD, Math.floor(this.cache.maxsize / 2))); + let frontier = [...stack].reverse(); + let fetched = 0; + let visited = 0; + while (frontier.length && fetched < budget && visited < 2 * budget) { + const missing = []; + const seen = new Set(); + for (const [a, b] of frontier) { + for (const side of [a, b]) { + if (typeof side === 'string' && !this.has(side) && !seen.has(side) && missing.length < budget - fetched) { + seen.add(side); + missing.push(side); + } + } + } + if (missing.length) { + await this.loadMany(missing); + fetched += missing.length; + } + const next = []; + for (const [a, b, acc] of frontier) { + const na = this.peek(a); + const nb = this.peek(b); + if (na === undefined || nb === undefined) continue; // beyond this round's budget + visited++; + next.push(...Trie.diffStep(na, nb, acc).pairs); + } + frontier = next; + } + } +} diff --git a/js/test/address.test.js b/js/test/address.test.js new file mode 100644 index 0000000..38217f6 --- /dev/null +++ b/js/test/address.test.js @@ -0,0 +1,33 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { keccak256Bytes, redundancyFor, sha256Ref, splitData, swarmRef } from '../src/address.js'; +import { toHex, utf8 } from '../src/bytes.js'; +import { fixture, pattern } from './helpers.js'; + +test('keccak256 is Ethereum keccak (the vendored js-sha3 sanity vector)', () => { + assert.equal(toHex(keccak256Bytes(utf8('hello'))), '1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8'); +}); + +test('Swarm references match swarmfs for every tree shape in the fixtures', () => { + for (const { size, ref } of fixture('swarm-addresses.json').cases) { + assert.equal(swarmRef(pattern(size)), ref, `${size} bytes`); + } +}); + +test('splitData lists every chunk, leaves first, intermediates with their spans', () => { + const data = pattern(4096 * 3 + 10); + const { root, chunks } = splitData(data); + assert.equal(chunks.length, 5); // four leaves and one intermediate + assert.equal(chunks.at(-1).address, root); + assert.equal(chunks.at(-1).span, data.length); + assert.equal(chunks.at(-1).payload.length, 4 * 32); +}); + +test('sha256 references are hex SHA-256, as Python MemoryBytesStore', async () => { + assert.equal(await sha256Ref(utf8('abc')), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'); +}); + +test('redundancy level: 1 while a blob fits a chunk, 0 above', () => { + assert.equal(redundancyFor(4096), 1); + assert.equal(redundancyFor(4097), 0); +}); diff --git a/js/test/api.test.js b/js/test/api.test.js new file mode 100644 index 0000000..1c75562 --- /dev/null +++ b/js/test/api.test.js @@ -0,0 +1,135 @@ +// Behaviour of the JavaScript API itself. + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { + ABSENT, BlobNotFoundError, MemoryBytesStore, MemoryPointer, RecordStore, RecordUnavailable, +} from '../src/index.js'; +import { collect } from './helpers.js'; + +test('reads see staged writes; commit makes them the root', async () => { + const store = new RecordStore(new MemoryBytesStore()); + store.put('users/alice', { name: 'Alice' }); + assert.deepEqual(await store.get('users/alice'), { name: 'Alice' }); + assert.equal(store.root, null); + const root = await store.commit(); + assert.match(root, /^[0-9a-f]{64}$/); + assert.equal(store.stagedCount, 0); + assert.deepEqual(await RecordStore.at(root, store.blobs).get('users/alice'), { name: 'Alice' }); +}); + +test('absent keys: get is undefined, has is false, delete is false; null is a value', async () => { + const store = new RecordStore(new MemoryBytesStore()); + store.put('n', null); + await store.commit(); + assert.equal(await store.get('n'), null); + assert.equal(await store.has('n'), true); + assert.equal(await store.get('missing'), undefined); + assert.equal(await store.has('missing'), false); + assert.equal(await store.delete('missing'), false); + assert.equal(store.stagedCount, 0); +}); + +test('records are copies: changing what get returned or what was put changes nothing stored', async () => { + const store = new RecordStore(new MemoryBytesStore()); + const value = { list: [1, 2] }; + store.put('k', value); + value.list.push(3); + const read = await store.get('k'); + read.list.push(4); + assert.deepEqual(await store.get('k'), { list: [1, 2] }); +}); + +test('equal content gives an equal root, whatever the history', async () => { + const a = new RecordStore(new MemoryBytesStore()); + for (const k of ['c', 'a', 'b']) a.put(k, k); + const b = new RecordStore(new MemoryBytesStore()); + b.put('a', 'a'); + b.put('x', 'temporary'); + await b.commit(); + b.put('b', 'b'); + b.put('c', 'c'); + await b.delete('x'); + assert.equal(await a.commit(), await b.commit()); +}); + +test('keys and items are sorted by code point, filtered by prefix, staged changes included', async () => { + const store = new RecordStore(new MemoryBytesStore()); + for (const k of ['b', '😀', '￿', 'a/2', 'a/1']) store.put(k, k); + await store.commit(); + store.put('a/0', 'staged'); + await store.delete('a/2'); + assert.deepEqual(await collect(store.keys()), ['a/0', 'a/1', 'b', '￿', '😀']); + assert.deepEqual(await collect(store.items('a/')), [['a/0', 'staged'], ['a/1', 'a/1']]); +}); + +test('getMany reads several records, leaving out absent keys', async () => { + const store = new RecordStore(new MemoryBytesStore()); + for (let i = 0; i < 50; i++) store.put(`k${i}`, i); + await store.commit(); + store.put('k1', 'staged'); + const got = await store.getMany(['k0', 'k1', 'k49', 'nope']); + assert.equal(got.size, 3); + assert.equal(got.get('k0'), 0); + assert.equal(got.get('k1'), 'staged'); + assert.equal(got.get('k49'), 49); +}); + +test('diff names what changed between two roots', async () => { + const store = new RecordStore(new MemoryBytesStore()); + for (let i = 0; i < 20; i++) store.put(`k${i}`, i); + const before = await store.commit(); + store.put('k3', 'changed'); + await store.delete('k4'); + store.put('new', true); + await store.commit(); + const changes = (await collect(RecordStore.at(before, store.blobs).diff(store.root))) + .sort((x, y) => (x[0] < y[0] ? -1 : 1)); + assert.deepEqual(changes, [['k3', 3, 'changed'], ['k4', 4, ABSENT], ['new', ABSENT, true]]); +}); + +test('a snapshot is read-only', async () => { + const snapshot = RecordStore.at(null, new MemoryBytesStore()); + assert.throws(() => snapshot.put('k', 1), TypeError); + await assert.rejects(snapshot.delete('k'), TypeError); +}); + +test('keys must be non-empty well-formed strings', () => { + const store = new RecordStore(new MemoryBytesStore()); + for (const bad of ['', '\ud800', 1, null]) assert.throws(() => store.put(bad, 1), TypeError); +}); + +test('a missing blob is reported as unavailable, never as absence', async () => { + const blobs = new MemoryBytesStore(); + const store = new RecordStore(blobs); + store.put('k', 'v'); + const root = await store.commit(); + const [[, valueRef]] = (await collect(store.refsUnder())).filter(([kind]) => kind === 'value'); + blobs.blobs.delete(valueRef); + await assert.rejects(RecordStore.at(root, blobs).get('k'), (e) => e instanceof RecordUnavailable && e.cause instanceof BlobNotFoundError); + blobs.blobs.clear(); + await assert.rejects(RecordStore.at(root, blobs).get('k'), RecordUnavailable); +}); + +test('two writers sharing a pointer converge with reconcile', async () => { + const blobs = new MemoryBytesStore(); + const pointer = new MemoryPointer(); + const a = await RecordStore.open(blobs, { pointer }); + const b = await RecordStore.open(blobs, { pointer }); + a.put('from/a', 1); + b.put('from/b', 2); + await a.commit({ reconcile: true }); + const root = await b.commit({ reconcile: true }); // the pointer moved under b: merge + assert.equal(await pointer.get(), root); + const merged = RecordStore.at(root, blobs); + assert.deepEqual(await collect(merged.items()), [['from/a', 1], ['from/b', 2]]); +}); + +test('commit with nothing staged returns the same root', async () => { + const store = new RecordStore(new MemoryBytesStore()); + store.put('k', 1); + const root = await store.commit(); + assert.equal(await store.commit(), root); + store.put('k', 1); // the same value again + assert.equal(await store.commit(), root); +}); diff --git a/js/test/canonical.test.js b/js/test/canonical.test.js new file mode 100644 index 0000000..76e60c2 --- /dev/null +++ b/js/test/canonical.test.js @@ -0,0 +1,53 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { canonicalBytes, canonicalJson, decodeValue, encodeValue, parseJson, pythonFloatRepr } from '../src/canonical.js'; +import { toHex } from '../src/bytes.js'; +import { fixture } from './helpers.js'; + +const canonical = fixture('canonical.json'); + +test('every value encodes to the bytes Python writes', () => { + for (const { value, hex } of canonical.values) { + assert.equal(toHex(canonicalBytes(value)), hex, `value ${canonicalJson(value)}`); + } +}); + +test('every record envelope encodes as Python writes it, and decodes back', () => { + for (const { value, hex } of canonical.records) { + assert.equal(toHex(encodeValue(value)), hex); + assert.deepEqual(decodeValue(encodeValue(value)), value); + } +}); + +test('large integers round-trip exactly through parseJson as BigInt', () => { + const big = parseJson('{"n": 9223372036854775808, "m": -1180591620717411303424, "s": 9007199254740991}'); + assert.equal(big.n, 9223372036854775808n); + assert.equal(big.m, -1180591620717411303424n); + assert.equal(big.s, 9007199254740991); + assert.equal(canonicalJson(big), '{"m":-1180591620717411303424,"n":9223372036854775808,"s":9007199254740991}'); +}); + +test('integral numbers below 2**53 are written as integers (the documented divergence from Python floats)', () => { + assert.equal(canonicalJson(1.0), '1'); + assert.equal(canonicalJson(-0), '0'); + assert.equal(canonicalJson(parseJson('1.0')), '1'); // Python wrote "1.0"; JavaScript cannot tell +}); + +test('other numbers follow Python float repr', () => { + const cases = [[0.1, '0.1'], [1e-5, '1e-05'], [1e-4, '0.0001'], [1e16, '1e+16'], [1e15 + 0.5, '1000000000000000.5'], + [2 ** 53, '9007199254740992.0'], [5e-324, '5e-324'], [-1e100, '-1e+100'], [1.5e-300, '1.5e-300'], [123.456, '123.456']]; + for (const [x, text] of cases) assert.equal(pythonFloatRepr(x), text); +}); + +test('object keys sort by code point, as Python sorts str', () => { + // UTF-16 order would put the emoji (a surrogate pair, 0xD83D...) before U+FFFF. + assert.equal(canonicalJson({ '😀': 1, '￿': 2, z: 3, é: 4 }), '{"z":3,"é":4,"￿":2,"😀":1}'); +}); + +test('what JSON cannot represent is refused, as in Python', () => { + for (const bad of [NaN, Infinity, -Infinity, undefined, () => 1, Symbol('s'), new Date(0), new Map(), + { a: undefined }, [undefined], '\ud800', { '\udc00': 1 }]) { + assert.throws(() => canonicalBytes(bad), `${String(bad)} should be refused`); + } + assert.throws(() => canonicalBytes([1, , 3])); // eslint-disable-line no-sparse-arrays +}); diff --git a/js/test/feeds.test.js b/js/test/feeds.test.js new file mode 100644 index 0000000..83dfca8 --- /dev/null +++ b/js/test/feeds.test.js @@ -0,0 +1,235 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { + FeedTransportError, MemoryBytesStore, MemoryPointer, ProofError, ProviderFeedTransport, RecordStore, + SwarmFeedPointer, VerificationError, addressOf, feedIdentifier, socAddress, topicOf, + verifyEquivocation, verifyFeedUpdate, +} from '../src/index.js'; +import { buildFeedUpdate, verifySoc } from '../src/feeds.js'; +import { concatBytes, fromHex, toHex } from '../src/bytes.js'; +import { collect, fixture } from './helpers.js'; + +const feeds = fixture('feeds.json'); +const noWait = async () => {}; + +// -- cross-language ----------------------------------------------------------- + +test('owners and topics derive as in Python (a 64-hex name is hashed too)', () => { + for (const { key, owner } of feeds.owners) assert.equal(toHex(addressOf(key)), owner); + for (const { name, topic } of feeds.topics) assert.equal(toHex(topicOf(name)), topic); +}); + +test('identifiers and update addresses match Python', () => { + for (const a of feeds.addresses) { + const id = feedIdentifier(fromHex(a.topic), a.index); + assert.equal(toHex(id), a.identifier); + assert.equal(toHex(socAddress(id, fromHex(a.owner))), a.address); + } +}); + +test('signed updates are byte-identical to the ones swarmfs writes', async () => { + for (const u of feeds.updates) { + const built = await buildFeedUpdate(u.key, fromHex(u.topic), u.index, u.root, u.timestamp); + assert.equal(toHex(built.identifier), u.identifier); + assert.equal(toHex(built.address), u.address); + assert.equal(toHex(built.soc), u.soc, `update ${u.index}: same signature, same bytes`); + } +}); + +test('updates Python published verify offline to what Python read from them', () => { + for (const u of feeds.updates) assert.deepEqual(verifyFeedUpdate(u.envelope, u.owner, u.topic), u.verified); + const first = feeds.updates[0]; + assert.deepEqual(verifyFeedUpdate(first.envelope, `0x${first.owner.toUpperCase()}`, 'recordstore/test'), first.verified); +}); + +test('an equivocation is proven, and a sequence is not one', () => { + const { a, b, index } = feeds.equivocation; + const [ua, ub] = [feeds.updates[a], feeds.updates[b]]; + assert.equal(verifyEquivocation(ua.envelope, ub.envelope, ua.owner, ua.topic), index); + assert.throws(() => verifyEquivocation(feeds.updates[0].envelope, ua.envelope, ua.owner, ua.topic), ProofError); + assert.throws(() => verifyEquivocation(ua.envelope, ua.envelope, ua.owner, ua.topic), ProofError); +}); + +test('every update Python refuses is refused', () => { + for (const { name, envelope } of feeds.refused) { + assert.throws(() => verifyFeedUpdate(envelope, envelope.owner, feeds.topics[0].topic), ProofError, name); + } +}); + +// -- the pointer, against an in-memory feed ----------------------------------- + +/** A feed store that checks uploads as Bee does, and can misbehave like a light node. */ +class MemoryFeedTransport { + constructor({ chunkFailures = 0, lookups = false, staleLookups = 0, seed = 1 } = {}) { + this.chunks = new Map(); + this.chunkFailures = chunkFailures; + this.staleLookups = staleLookups; + this.seed = seed; + this.reads = 0; + if (lookups) this.feedHead = this._feedHead; + } + + random() { + this.seed = (this.seed * 1103515245 + 12345) % 2 ** 31; + return this.seed / 2 ** 31; + } + + async readSoc(address) { + this.reads++; + if (this.random() < this.chunkFailures) throw new FeedTransportError('read chunk failed', 500); + const soc = this.chunks.get(address); + if (!soc) throw new FeedTransportError('not found', 404); + return soc; + } + + async writeSoc(owner, identifier, signature, data) { + const id = fromHex(identifier); + const ownerB = fromHex(owner); + const address = socAddress(id, ownerB); + const soc = concatBytes(id, fromHex(signature), data); + verifySoc(soc, ownerB, address); // Bee refuses an update the owner did not sign + this.chunks.set(toHex(address), soc); // and, like Bee, accepts a second one at a used index + } + + async _feedHead(owner, topic) { + if (this.random() < this.staleLookups) { + if (this.random() < 0.5) throw new FeedTransportError('lookup failed', 404); + return 0; // stale-early + } + let i = 0; + while (this.chunks.has(toHex(socAddress(feedIdentifier(fromHex(topic), i), fromHex(owner))))) i++; + return i === 0 ? null : i - 1; + } +} + +const KEY = '3b'.repeat(32); +const OTHER = '4c'.repeat(32); +const ROOT = (n) => n.toString(16).padStart(2, '0').repeat(32); + +test('an empty feed reads as null; a published root reads back, also to a follower', async () => { + const feed = new MemoryFeedTransport(); + const writer = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + assert.equal(await writer.get(), null); + await writer.set(ROOT(1)); + assert.equal(await writer.get(), ROOT(1)); + const follower = new SwarmFeedPointer(feed, 'notes', { owner: writer.owner, sleep: noWait }); + assert.equal(await follower.get(), ROOT(1)); + assert.equal(follower.owner, toHex(addressOf(KEY))); + await assert.rejects(follower.set(ROOT(2)), /signer/); +}); + +test('updates go to consecutive indexes; a cold writer finds the tip by probing', async () => { + const feed = new MemoryFeedTransport(); + const writer = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + for (let i = 1; i <= 5; i++) await writer.set(ROOT(i)); + const cold = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + await cold.set(ROOT(6)); + assert.equal(feed.chunks.size, 6); + const follower = new SwarmFeedPointer(feed, 'notes', { owner: writer.owner, sleep: noWait }); + assert.equal(await follower.get(), ROOT(6)); + const envelopes = await follower.updates(); + assert.deepEqual(envelopes.map((e) => verifyFeedUpdate(e, writer.owner, 'notes').root), [1, 2, 3, 4, 5, 6].map(ROOT)); +}); + +test('flaky chunk reads and stale or failed lookups still resolve the latest root', async () => { + const feed = new MemoryFeedTransport({ lookups: true }); + const writer = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + for (let i = 1; i <= 9; i++) await writer.set(ROOT(i)); + feed.chunkFailures = 0.3; + feed.staleLookups = 0.5; + let latest = ROOT(9); + for (let trial = 0; trial < 20; trial++) { + const follower = new SwarmFeedPointer(feed, 'notes', { owner: writer.owner, sleep: noWait, maxLookupRetries: 30 }); + assert.equal(await follower.get(), latest); + const next = ROOT(10 + trial); + await writer.set(next); + assert.equal(await follower.get(), latest, 'within the cache lifetime the pointer does not look again'); + assert.equal(await follower._getFresh(), next); + latest = next; + } +}); + +test('a tampered update is an error, not a missing one', async () => { + const feed = new MemoryFeedTransport(); + const writer = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + await writer.set(ROOT(1)); + const [address, soc] = [...feed.chunks][0]; + const forged = Uint8Array.from(soc); + forged[forged.length - 1] ^= 1; + feed.chunks.set(address, forged); + const follower = new SwarmFeedPointer(feed, 'notes', { owner: writer.owner, sleep: noWait }); + await assert.rejects(follower.get(), VerificationError); +}); + +test('compareAndSet moves the feed only from the expected root', async () => { + const feed = new MemoryFeedTransport(); + const p = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + await p.set(ROOT(1)); + assert.equal(await p.compareAndSet(ROOT(2), ROOT(3)), false); + assert.equal(await p.compareAndSet(ROOT(1), ROOT(3)), true); + assert.equal(await p.get(), ROOT(3)); +}); + +test('a store behind a feed: a follower opens it, two writers reconcile', async () => { + const blobs = new MemoryBytesStore({ addressing: 'swarm' }); + const feed = new MemoryFeedTransport(); + const pointer = () => new SwarmFeedPointer(feed, 'shared', { signer: KEY, sleep: noWait }); + const a = await RecordStore.open(blobs, { pointer: pointer() }); + const b = await RecordStore.open(blobs, { pointer: pointer() }); + a.put('from/a', 1); + b.put('from/b', 2); + await a.commit({ reconcile: true }); + const root = await b.commit({ reconcile: true }); // b's pointer finds a's update and merges + const follower = await RecordStore.open(blobs, { + pointer: new SwarmFeedPointer(feed, 'shared', { owner: toHex(addressOf(KEY)), sleep: noWait }), + }); + assert.equal(follower.root, root); + assert.deepEqual(await collect(follower.items()), [['from/a', 1], ['from/b', 2]]); +}); + +test('a feed is its owner’s alone: another key writes another feed', async () => { + const feed = new MemoryFeedTransport(); + await new SwarmFeedPointer(feed, 'notes', { signer: OTHER, sleep: noWait }).set(ROOT(7)); + const mine = new SwarmFeedPointer(feed, 'notes', { owner: toHex(addressOf(KEY)), sleep: noWait }); + assert.equal(await mine.get(), null); +}); + +// -- through Freedom's provider --------------------------------------------- + +/** window.swarm's swarm_readSingleOwnerChunk over the in-memory feed. */ +function providerOver(feed) { + return { + async request({ method, params }) { + assert.equal(method, 'swarm_readSingleOwnerChunk'); + const soc = feed.chunks.get(params.address); + if (!soc) throw Object.assign(new Error('not found'), { data: { reason: 'chunk_not_found' } }); + const payload = soc.subarray(105); + return { + data: btoa(String.fromCharCode(...payload)), encoding: 'base64', + span: payload.length, reference: params.address, + identifier: toHex(soc.subarray(0, 32)), signature: `0x${toHex(soc.subarray(32, 97))}`, + }; + }, + }; +} + +test('through window.swarm: any feed reads (and is checked again); writing waits for #647', async () => { + const feed = new MemoryFeedTransport(); + const writer = new SwarmFeedPointer(feed, 'notes', { signer: KEY, sleep: noWait }); + for (let i = 1; i <= 3; i++) await writer.set(ROOT(i)); + const transport = new ProviderFeedTransport(providerOver(feed)); + const reader = new SwarmFeedPointer(transport, 'notes', { owner: writer.owner, sleep: noWait }); + assert.equal(await reader.get(), ROOT(3)); + const own = new SwarmFeedPointer(transport, 'notes', { signer: KEY, sleep: noWait }); + await assert.rejects(own.set(ROOT(4)), /#647/); +}); + +test('MemoryPointer and SwarmFeedPointer are interchangeable to a store', async () => { + for (const pointer of [new MemoryPointer(), new SwarmFeedPointer(new MemoryFeedTransport(), 't', { signer: KEY, sleep: noWait })]) { + const store = await RecordStore.open(new MemoryBytesStore(), { pointer }); + store.put('k', 'v'); + assert.equal(await pointer.get(), null); + const root = await store.commit(); + assert.equal(await pointer.get(), root); + } +}); diff --git a/js/test/fixtures/canonical.json b/js/test/fixtures/canonical.json new file mode 100644 index 0000000..f83f736 --- /dev/null +++ b/js/test/fixtures/canonical.json @@ -0,0 +1,296 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "values": [ + { + "value": null, + "hex": "6e756c6c" + }, + { + "value": true, + "hex": "74727565" + }, + { + "value": false, + "hex": "66616c7365" + }, + { + "value": "", + "hex": "2222" + }, + { + "value": "plain", + "hex": "22706c61696e22" + }, + { + "value": "quote \" and backslash \\", + "hex": "2271756f7465205c2220616e64206261636b736c617368205c5c22" + }, + { + "value": "tab\tnewline\ncr\r", + "hex": "227461625c746e65776c696e655c6e63725c7222" + }, + { + "value": "controls \u0000\u0001\u001f", + "hex": "22636f6e74726f6c73205c75303030305c75303030315c75303031667f22" + }, + { + "value": "\b\f", + "hex": "225c625c6622" + }, + { + "value": "é 中文 😀", + "hex": "22c3a920e4b8ade6968720f09f988022" + }, + { + "value": "

", + "hex": "22e280a8e280a922" + }, + { + "value": "￿", + "hex": "22efbfbf22" + }, + { + "value": "/", + "hex": "222f22" + }, + { + "value": "", + "hex": "223c2f7363726970743e22" + }, + { + "value": 0, + "hex": "30" + }, + { + "value": 1, + "hex": "31" + }, + { + "value": -1, + "hex": "2d31" + }, + { + "value": 42, + "hex": "3432" + }, + { + "value": 2147483648, + "hex": "32313437343833363438" + }, + { + "value": -2147483649, + "hex": "2d32313437343833363439" + }, + { + "value": 9007199254740991, + "hex": "39303037313939323534373430393931" + }, + { + "value": -9007199254740991, + "hex": "2d39303037313939323534373430393931" + }, + { + "value": 9007199254740992, + "hex": "39303037313939323534373430393932" + }, + { + "value": 9223372036854775808, + "hex": "39323233333732303336383534373735383038" + }, + { + "value": -1180591620717411303424, + "hex": "2d31313830353931363230373137343131333033343234" + }, + { + "value": 1000000000000000000000000000000, + "hex": "31303030303030303030303030303030303030303030303030303030303030" + }, + { + "value": 0.1, + "hex": "302e31" + }, + { + "value": -2.5, + "hex": "2d322e35" + }, + { + "value": 0.30000000000000004, + "hex": "302e3330303030303030303030303030303034" + }, + { + "value": 1.0000000000000002, + "hex": "312e30303030303030303030303030303032" + }, + { + "value": 123.456, + "hex": "3132332e343536" + }, + { + "value": 0.0001, + "hex": "302e30303031" + }, + { + "value": 0.0001234, + "hex": "302e30303031323334" + }, + { + "value": 1e-05, + "hex": "31652d3035" + }, + { + "value": 1e-07, + "hex": "31652d3037" + }, + { + "value": 2.5e-05, + "hex": "322e35652d3035" + }, + { + "value": 1.5e-300, + "hex": "312e35652d333030" + }, + { + "value": 5e-324, + "hex": "35652d333234" + }, + { + "value": 1000000000000000.5, + "hex": "313030303030303030303030303030302e35" + }, + { + "value": 1e+16, + "hex": "31652b3136" + }, + { + "value": 1e+21, + "hex": "31652b3231" + }, + { + "value": 1e+22, + "hex": "31652b3232" + }, + { + "value": 1.7976931348623157e+308, + "hex": "312e37393736393331333438363233313537652b333038" + }, + { + "value": 9007199254740992.0, + "hex": "393030373139393235343734303939322e30" + }, + { + "value": -1e+100, + "hex": "2d31652b313030" + }, + { + "value": 3.14159265358979, + "hex": "332e3134313539323635333538393739" + }, + { + "value": 100.5, + "hex": "3130302e35" + }, + { + "value": {}, + "hex": "7b7d" + }, + { + "value": [], + "hex": "5b5d" + }, + { + "value": { + "b": 1, + "a": 2 + }, + "hex": "7b2261223a322c2262223a317d" + }, + { + "value": { + "z": 1, + "é": 2, + "😀": 3, + "￿": 4, + "A": 5, + "a": 6, + "": 7, + "aa": 8 + }, + "hex": "7b22223a372c2241223a352c2261223a362c226161223a382c227a223a312c22c3a9223a322c22efbfbf223a342c22f09f9880223a337d" + }, + { + "value": { + "nested": { + "list": [ + 1, + "two", + null, + true, + false, + { + "x": [] + } + ], + "empty": {} + } + }, + "hex": "7b226e6573746564223a7b22656d707479223a7b7d2c226c697374223a5b312c2274776f222c6e756c6c2c747275652c66616c73652c7b2278223a5b5d7d5d7d7d" + }, + { + "value": [ + null, + true, + false, + 0, + "", + [], + {} + ], + "hex": "5b6e756c6c2c747275652c66616c73652c302c22222c5b5d2c7b7d5d" + }, + { + "value": { + "users": [ + { + "name": "Alice", + "role": "admin" + }, + { + "name": "Bob", + "tags": [ + "x", + "y" + ] + } + ] + }, + "hex": "7b227573657273223a5b7b226e616d65223a22416c696365222c22726f6c65223a2261646d696e227d2c7b226e616d65223a22426f62222c2274616773223a5b2278222c2279225d7d5d7d" + } + ], + "records": [ + { + "value": null, + "hex": "7b22727376223a312c2276616c223a6e756c6c7d" + }, + { + "value": "x", + "hex": "7b22727376223a312c2276616c223a2278227d" + }, + { + "value": 1, + "hex": "7b22727376223a312c2276616c223a317d" + }, + { + "value": 0.5, + "hex": "7b22727376223a312c2276616c223a302e357d" + }, + { + "value": { + "a": [ + 1, + 2 + ] + }, + "hex": "7b22727376223a312c2276616c223a7b2261223a5b312c325d7d7d" + } + ] +} diff --git a/js/test/fixtures/feeds.json b/js/test/fixtures/feeds.json new file mode 100644 index 0000000..c5432f0 --- /dev/null +++ b/js/test/fixtures/feeds.json @@ -0,0 +1,234 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "owners": [ + { + "key": "1111111111111111111111111111111111111111111111111111111111111111", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a" + }, + { + "key": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a701", + "owner": "087bf3818c89420a2be42c4e2affb4df77d402fa" + } + ], + "topics": [ + { + "name": "recordstore/test", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13" + }, + { + "name": "loop-twonode-1791730596", + "topic": "d3905aa3645eaebb27eb1c091cd8b9a1a610a5f16a3668fdb630b511d95d32d1" + }, + { + "name": "abababababababababababababababababababababababababababababababab", + "topic": "edfeca1e60bc3e32447bebed8111663879980928231fc72d42ad4ff98f1cfa94" + }, + { + "name": "é😀", + "topic": "c387a34b042bca8dfbc38dbc6489ce0084ad0f6f09690bd69bdfc837d104de05" + } + ], + "addresses": [ + { + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "index": 0, + "identifier": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb9", + "address": "e46a6ee5abeb540d02777e2ace19e24aaa88b86b2b882c9752210e0c22f0b813" + }, + { + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "index": 1, + "identifier": "f12cb2f786e4974b850928d4a68427975a4bce7fae8e548071c682697a359c87", + "address": "2c6b7a2914df8e19ee860c98fb1ab6f40c908643244a87b9ddab3fa45e1f2222" + }, + { + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "index": 255, + "identifier": "a6ae3f1de883606377d0b8858b05b3da4aa6ac903f4814ec427048f5f20ac039", + "address": "1cc6f9a9d6768e4df05a8fa8522fb2cb78e10b83782834da6204aea2add073e1" + }, + { + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "index": 4294967301, + "identifier": "647f672f65833e9d91bc44137ec3ee5dd89ea6f57b791c347f6f0f91b67f3731", + "address": "b2c3ee08c34cf0aa7b15b3b8c200ec015f9c826851a7817b31139075452355db" + } + ], + "updates": [ + { + "key": "1111111111111111111111111111111111111111111111111111111111111111", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 0, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1790000000, + "identifier": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb9", + "address": "e46a6ee5abeb540d02777e2ace19e24aaa88b86b2b882c9752210e0c22f0b813", + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e471f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 0, + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e471f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + }, + "verified": { + "index": 0, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1790000000 + } + }, + { + "key": "1111111111111111111111111111111111111111111111111111111111111111", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 7, + "root": "c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3", + "timestamp": 1790000123, + "identifier": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521", + "address": "6684ad0551f5b22822518b055b6aee787f0eed07177d96678debff7e0030c7a4", + "soc": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521e1018c1c4c3a88e407805b41f276d2fefa654fd0f757a83e1018a9d3d5b87983514457b9edafac538a7b8999be372525ee93438ee3990640b1c26c64f5fc9a791b2800000000000000000000006ab13bfbc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 7, + "soc": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521e1018c1c4c3a88e407805b41f276d2fefa654fd0f757a83e1018a9d3d5b87983514457b9edafac538a7b8999be372525ee93438ee3990640b1c26c64f5fc9a791b2800000000000000000000006ab13bfbc3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3" + }, + "verified": { + "index": 7, + "root": "c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3c3", + "timestamp": 1790000123 + } + }, + { + "key": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a701", + "topic": "d3905aa3645eaebb27eb1c091cd8b9a1a610a5f16a3668fdb630b511d95d32d1", + "index": 3, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1800000000, + "identifier": "f9f4904418b128833442d5ba2885ab3d8dcaa5f896d93ccf6bb31b89944f6a09", + "address": "9a86f6793b383f8220861b1fd104347026b198b3a9ac0bcec6dc2d84ec36910f", + "soc": "f9f4904418b128833442d5ba2885ab3d8dcaa5f896d93ccf6bb31b89944f6a09cccde17a6ec27456d19bf92680321b273f63bbbb0124ad98e8e73963e42b61e61107d71456217b44e6f92c01d73b7a8502f07412370ea7ecf7948931f46a16031c2800000000000000000000006b49d2005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "owner": "087bf3818c89420a2be42c4e2affb4df77d402fa", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "087bf3818c89420a2be42c4e2affb4df77d402fa", + "topic": "d3905aa3645eaebb27eb1c091cd8b9a1a610a5f16a3668fdb630b511d95d32d1", + "index": 3, + "soc": "f9f4904418b128833442d5ba2885ab3d8dcaa5f896d93ccf6bb31b89944f6a09cccde17a6ec27456d19bf92680321b273f63bbbb0124ad98e8e73963e42b61e61107d71456217b44e6f92c01d73b7a8502f07412370ea7ecf7948931f46a16031c2800000000000000000000006b49d2005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + }, + "verified": { + "index": 3, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1800000000 + } + }, + { + "key": "1111111111111111111111111111111111111111111111111111111111111111", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 7, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1790000124, + "identifier": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521", + "address": "6684ad0551f5b22822518b055b6aee787f0eed07177d96678debff7e0030c7a4", + "soc": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521342f33ac374698320485184ea8a294f848e60385c590ff5ea29fd079ff1832892d75bff5c3b5c34eda9e798b5d76002dda2c74ac005c5cdde0b3bf0ec52047951c2800000000000000000000006ab13bfc5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 7, + "soc": "a6e371abc13b2766f8bdea58c793b88a9b2fb81259d69fe29acbae1cdc65a521342f33ac374698320485184ea8a294f848e60385c590ff5ea29fd079ff1832892d75bff5c3b5c34eda9e798b5d76002dda2c74ac005c5cdde0b3bf0ec52047951c2800000000000000000000006ab13bfc5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + }, + "verified": { + "index": 7, + "root": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "timestamp": 1790000124 + } + }, + { + "key": "1111111111111111111111111111111111111111111111111111111111111111", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 9, + "root": "77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777", + "timestamp": 1790000200, + "identifier": "37685181963c9ae62b50322900d43741262787a7d9c28e9086f70eb06a8ea034", + "address": "2a8ea65842dd1b7cb2956e9e2622bc83cb77c99729068cf83329f50f4b6c8b3a", + "soc": "37685181963c9ae62b50322900d43741262787a7d9c28e9086f70eb06a8ea034403382ff5634bc1f2519e5339e16cb011ab46e5572379b220ccc11e7ceffdf4973ec86a11af18e9cea12b370b63eefb8f0d0fe37700015ebfd50f6fc580032081b4800000000000000000000006ab13c4877777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777", + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 9, + "soc": "37685181963c9ae62b50322900d43741262787a7d9c28e9086f70eb06a8ea034403382ff5634bc1f2519e5339e16cb011ab46e5572379b220ccc11e7ceffdf4973ec86a11af18e9cea12b370b63eefb8f0d0fe37700015ebfd50f6fc580032081b4800000000000000000000006ab13c4877777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777" + }, + "verified": { + "index": 9, + "root": "77777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777", + "timestamp": 1790000200 + } + } + ], + "equivocation": { + "a": 1, + "b": 3, + "index": 7 + }, + "refused": [ + { + "name": "signature byte flipped", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 0, + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e001f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + } + }, + { + "name": "payload byte flipped", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 0, + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e471f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a00" + } + }, + { + "name": "claims another index", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "19e7e376e7c213b7e7e7e46cc70a5dd086daff2a", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 1, + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e471f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + } + }, + { + "name": "claims another owner", + "envelope": { + "format": "recordstore-feed-update", + "version": 1, + "owner": "0000000000000000000000000000000000000000", + "topic": "596916f18814d78dae04969ddfd46f793d05446f2aa60360dabfb26e831d9e13", + "index": 0, + "soc": "0d3f758d9a0a654f34f3e2f436236fbeb945622d5041d35fdcbf822cbaecfeb94e471f0b5e11d98f30aace65e145f155398e3818a6cef186a660968e1e171aef1ea9a591ab7188523ce58d4c40f366d531b74ad09dc94c06449f3bd69a6156461c2800000000000000000000006ab13b805a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a" + } + } + ] +} diff --git a/js/test/fixtures/merges.json b/js/test/fixtures/merges.json new file mode 100644 index 0000000..73a9b1b --- /dev/null +++ b/js/test/fixtures/merges.json @@ -0,0 +1,1190 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "resolver": "ours wins: DELETE if ours is ABSENT, else ours", + "cases": [ + { + "name": "disjoint/sha256", + "addressing": "sha256", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k1", + "value": "ours" + }, + { + "op": "delete", + "key": "k2" + }, + { + "op": "put", + "key": "new/ours", + "value": 1 + } + ], + "theirs": [ + { + "op": "put", + "key": "k3", + "value": "theirs" + }, + { + "op": "delete", + "key": "k4" + }, + { + "op": "put", + "key": "new/theirs", + "value": 2 + } + ], + "roots": { + "base": "5d8608fd97e8d12a397a5f8066a952f6cb347e6fb5a62f5e0590c3f9ed98a8d9", + "ours": "7f8b141faeaa443edcf3834970e2ec50d8f0e49264f6f4c9937b55291790ce51", + "theirs": "f803321a6a18e1836cec9d9d79eba5c4cf84cadb7bc8868367c21668b5b3ec87" + }, + "merged": "69095080f2ee342d582a83b671644828fc11d64cb9fe27d8599e879d0aeef6f0" + }, + { + "name": "same-change/sha256", + "addressing": "sha256", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k5", + "value": "both" + }, + { + "op": "delete", + "key": "k6" + } + ], + "theirs": [ + { + "op": "put", + "key": "k5", + "value": "both" + }, + { + "op": "delete", + "key": "k6" + }, + { + "op": "put", + "key": "k7", + "value": "theirs" + } + ], + "roots": { + "base": "5d8608fd97e8d12a397a5f8066a952f6cb347e6fb5a62f5e0590c3f9ed98a8d9", + "ours": "cd7f9abb508a208fcc27df1e3a1f721863390f62971bfb923110845552bcec81", + "theirs": "d7e07af3d119833eb80d453c38f301a9b894c7b9fbb1ef6a3c5df809cf7f0a2e" + }, + "merged": "d7e07af3d119833eb80d453c38f301a9b894c7b9fbb1ef6a3c5df809cf7f0a2e" + }, + { + "name": "conflict/sha256", + "addressing": "sha256", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k8", + "value": "ours" + }, + { + "op": "delete", + "key": "shared/a" + }, + { + "op": "put", + "key": "x", + "value": 1 + } + ], + "theirs": [ + { + "op": "put", + "key": "k8", + "value": "theirs" + }, + { + "op": "put", + "key": "shared/a", + "value": 99 + }, + { + "op": "put", + "key": "x", + "value": 2 + } + ], + "roots": { + "base": "5d8608fd97e8d12a397a5f8066a952f6cb347e6fb5a62f5e0590c3f9ed98a8d9", + "ours": "bb7c838e1cd11980a607370580806cb07f4f3a7d238ff1a8acc9c983f050c373", + "theirs": "4e5f0d39c4cb0604b4363d3cbc2d3e9b47b7fbdc61a64bbc6a718d465ef846ce" + }, + "conflicts": [ + "k8", + "shared/a", + "x" + ], + "merged_ours_wins": "bb7c838e1cd11980a607370580806cb07f4f3a7d238ff1a8acc9c983f050c373" + }, + { + "name": "disjoint/swarm", + "addressing": "swarm", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k1", + "value": "ours" + }, + { + "op": "delete", + "key": "k2" + }, + { + "op": "put", + "key": "new/ours", + "value": 1 + } + ], + "theirs": [ + { + "op": "put", + "key": "k3", + "value": "theirs" + }, + { + "op": "delete", + "key": "k4" + }, + { + "op": "put", + "key": "new/theirs", + "value": 2 + } + ], + "roots": { + "base": "06b870770bc54d34705254f353957716f6ac96b581debcda6f9808710215d8c6", + "ours": "e5943ea5d97c41e100de99e2b194de8bd08ce07bd79bdb97e44a0bdfd30d9b78", + "theirs": "d3908b085e14fa4bf40fa2a5495af9557ee2cf8f95c69798d3927503ce783a16" + }, + "merged": "e72b92340688ed33744fd61baf6b70e4aea66d8c583f48850a32bfbfdee05b30" + }, + { + "name": "same-change/swarm", + "addressing": "swarm", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k5", + "value": "both" + }, + { + "op": "delete", + "key": "k6" + } + ], + "theirs": [ + { + "op": "put", + "key": "k5", + "value": "both" + }, + { + "op": "delete", + "key": "k6" + }, + { + "op": "put", + "key": "k7", + "value": "theirs" + } + ], + "roots": { + "base": "06b870770bc54d34705254f353957716f6ac96b581debcda6f9808710215d8c6", + "ours": "dc188c1e71f8b7b68dde08673cc5493eb8406a4a9bad7a632cbccb50334fd5a4", + "theirs": "1a4421bf9d87d2b7418800f8d03cd22f783b730ddb7f58f945e12a029fbe9753" + }, + "merged": "1a4421bf9d87d2b7418800f8d03cd22f783b730ddb7f58f945e12a029fbe9753" + }, + { + "name": "conflict/swarm", + "addressing": "swarm", + "base": [ + { + "op": "put", + "key": "k0", + "value": { + "n": 0 + } + }, + { + "op": "put", + "key": "k1", + "value": { + "n": 1 + } + }, + { + "op": "put", + "key": "k2", + "value": { + "n": 2 + } + }, + { + "op": "put", + "key": "k3", + "value": { + "n": 3 + } + }, + { + "op": "put", + "key": "k4", + "value": { + "n": 4 + } + }, + { + "op": "put", + "key": "k5", + "value": { + "n": 5 + } + }, + { + "op": "put", + "key": "k6", + "value": { + "n": 6 + } + }, + { + "op": "put", + "key": "k7", + "value": { + "n": 7 + } + }, + { + "op": "put", + "key": "k8", + "value": { + "n": 8 + } + }, + { + "op": "put", + "key": "k9", + "value": { + "n": 9 + } + }, + { + "op": "put", + "key": "k10", + "value": { + "n": 10 + } + }, + { + "op": "put", + "key": "k11", + "value": { + "n": 11 + } + }, + { + "op": "put", + "key": "k12", + "value": { + "n": 12 + } + }, + { + "op": "put", + "key": "k13", + "value": { + "n": 13 + } + }, + { + "op": "put", + "key": "k14", + "value": { + "n": 14 + } + }, + { + "op": "put", + "key": "k15", + "value": { + "n": 15 + } + }, + { + "op": "put", + "key": "k16", + "value": { + "n": 16 + } + }, + { + "op": "put", + "key": "k17", + "value": { + "n": 17 + } + }, + { + "op": "put", + "key": "k18", + "value": { + "n": 18 + } + }, + { + "op": "put", + "key": "k19", + "value": { + "n": 19 + } + }, + { + "op": "put", + "key": "shared/a", + "value": 1 + }, + { + "op": "put", + "key": "shared/b", + "value": 2 + }, + { + "op": "commit" + } + ], + "ours": [ + { + "op": "put", + "key": "k8", + "value": "ours" + }, + { + "op": "delete", + "key": "shared/a" + }, + { + "op": "put", + "key": "x", + "value": 1 + } + ], + "theirs": [ + { + "op": "put", + "key": "k8", + "value": "theirs" + }, + { + "op": "put", + "key": "shared/a", + "value": 99 + }, + { + "op": "put", + "key": "x", + "value": 2 + } + ], + "roots": { + "base": "06b870770bc54d34705254f353957716f6ac96b581debcda6f9808710215d8c6", + "ours": "8fff628e0fcfbf5bc6f4648c10e052e8b2850558b171dcd67cc81b8ef8594617", + "theirs": "98770a494e3f7514ad1f6891c7b545848eb314616b4ca7e78bedd1556f7e7196" + }, + "conflicts": [ + "k8", + "shared/a", + "x" + ], + "merged_ours_wins": "8fff628e0fcfbf5bc6f4648c10e052e8b2850558b171dcd67cc81b8ef8594617" + } + ] +} diff --git a/js/test/fixtures/proofs.json b/js/test/fixtures/proofs.json new file mode 100644 index 0000000..16697d9 --- /dev/null +++ b/js/test/fixtures/proofs.json @@ -0,0 +1,2855 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "cases": [ + { + "addressing": "sha256", + "first": [ + { + "op": "put", + "key": "log/1", + "value": { + "event": 1 + } + }, + { + "op": "put", + "key": "log/2", + "value": { + "event": 2 + } + }, + { + "op": "put", + "key": "log/3", + "value": { + "event": 3 + } + }, + { + "op": "put", + "key": "log/4", + "value": { + "event": 4 + } + }, + { + "op": "put", + "key": "log/5", + "value": { + "event": 5 + } + }, + { + "op": "put", + "key": "state/x", + "value": "old" + }, + { + "op": "put", + "key": "state/y", + "value": [ + 1, + 2 + ] + }, + { + "op": "put", + "key": "é", + "value": "é" + }, + { + "op": "put", + "key": "😀", + "value": null + }, + { + "op": "put", + "key": "users/alice", + "value": { + "n": "Alice" + } + }, + { + "op": "put", + "key": "users/al", + "value": 0.5 + }, + { + "op": "commit" + } + ], + "second": [ + { + "op": "put", + "key": "log/6", + "value": { + "event": 6 + } + }, + { + "op": "put", + "key": "state/x", + "value": "new" + }, + { + "op": "delete", + "key": "users/al" + }, + { + "op": "put", + "key": "zz", + "value": true + }, + { + "op": "commit" + } + ], + "roots": { + "a": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "b": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + "empty": { + "key": "anything", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": null, + "key": "anything", + "present": false, + "nodes": [], + "value": null + } + }, + "keys": [ + { + "key": "log/3", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "present": true, + "value": { + "event": 3 + } + }, + { + "key": "state/x", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "state/x", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2265336131316335666461363065373439336366613661323562656538623962316532633763346462313930383636636633316361363831373736616339393165222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2265373335636664306138613534363931343437643063623630383665366262633864626466323137356431313665316139626366363234316663373063393436227d" + ], + "value": "7b22727376223a312c2276616c223a226f6c64227d" + }, + "present": true, + "value": "old" + }, + { + "key": "users/alice", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "users/alice", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + }, + "present": true, + "value": { + "n": "Alice" + } + }, + { + "key": "users/al", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "users/al", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d" + ], + "value": "7b22727376223a312c2276616c223a302e357d" + }, + "present": true, + "value": 0.5 + }, + { + "key": "users/a", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "users/a", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/alicex", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "users/alicex", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "é", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "é", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "value": "7b22727376223a312c2276616c223a22c3a9227d" + }, + "present": true, + "value": "é" + }, + { + "key": "😀", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "😀", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2232306662646531306239386663633362396564366363653166316637616365663465343066356462636164633465353235396165613537366131333961393662227d" + ], + "value": "7b22727376223a312c2276616c223a6e756c6c7d" + }, + "present": true, + "value": null + }, + { + "key": "nope", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "nope", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "log/", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "zzz", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "key": "zzz", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/3", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "present": true, + "value": { + "event": 3 + } + }, + { + "key": "state/x", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "state/x", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2235643232636333316166373262343365646439383937313062306532303566343135313731313333363264303032313766633465663661306161663338616232222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238626138343432323735393662656538353831376436363833386438653034653963656230643436623766646337393232353234633165633539663339366133227d" + ], + "value": "7b22727376223a312c2276616c223a226e6577227d" + }, + "present": true, + "value": "new" + }, + { + "key": "users/alice", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "users/alice", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + }, + "present": true, + "value": { + "n": "Alice" + } + }, + { + "key": "users/al", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "users/al", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/a", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "users/a", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/alicex", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "users/alicex", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "é", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "é", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "value": "7b22727376223a312c2276616c223a22c3a9227d" + }, + "present": true, + "value": "é" + }, + { + "key": "😀", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "😀", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2232306662646531306239386663633362396564366363653166316637616365663465343066356462636164633465353235396165613537366131333961393662227d" + ], + "value": "7b22727376223a312c2276616c223a6e756c6c7d" + }, + "present": true, + "value": null + }, + { + "key": "nope", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "nope", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "zzz", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "zzz", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2263363061636538653238616363336437386566623533646632393063326335666439363063363833626265356164646336613463316639376166643636356537227d" + ], + "value": null + }, + "present": false, + "value": null + } + ], + "prefixes": [ + { + "prefix": "", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "", + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2265336131316335666461363065373439336366613661323562656538623962316532633763346462313930383636636633316361363831373736616339393165222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2230613331653765653936303835383837306564373365363732623931316565663764303631656336363238646530636366636338343435373230636163333335227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235333838373961663638373934343136626135643934343264666230653932666630373161393637343731653964613231316562313664303733623334663131227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236383132643736356331376565373730343931353931623337633439366530653264343632633132643834333766323462333463626465313539633037633235227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2262373331313832313335376466333964396663616635353966663262666638373062313635363933626361396665316238386661623332623935373735323437227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263393933333233336639646464616236663934363062636161366666626565653734383766376531316535333934636330353863616236636163633266623261227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2265373335636664306138613534363931343437643063623630383665366262633864626466323137356431313665316139626366363234316663373063393436227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2232306662646531306239386663633362396564366363653166316637616365663465343066356462636164633465353235396165613537366131333961393662227d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226f6c64227d", + "7b22727376223a312c2276616c223a22c3a9227d", + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a5b312c325d7d", + "7b22727376223a312c2276616c223a6e756c6c7d", + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "state/x", + "old" + ], + [ + "state/y", + [ + 1, + 2 + ] + ], + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ], + [ + "é", + "é" + ], + [ + "😀", + null + ] + ] + }, + { + "prefix": "log/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "log/", + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2230613331653765653936303835383837306564373365363732623931316565663764303631656336363238646530636366636338343435373230636163333335227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236383132643736356331376565373730343931353931623337633439366530653264343632633132643834333766323462333463626465313539633037633235227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2262373331313832313335376466333964396663616635353966663262666638373062313635363933626361396665316238386661623332623935373735323437227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263393933333233336639646464616236663934363062636161366666626565653734383766376531316535333934636330353863616236636163633266623261227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ] + ] + }, + { + "prefix": "users/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "users/", + "nodes": [ + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "values": [ + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "users/al", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "users/al", + "nodes": [ + "7b2263223a7b223639223a2238353962306465643966386431393639343733633334313833656234626438343264636330663439616238616633653635386639353965303861343665393866227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2238333335643137626236313966313332666262333038306538333436383561383265383230316534613162363937386332316536343638393035323739656531227d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "values": [ + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "state/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "state/", + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2265336131316335666461363065373439336366613661323562656538623962316532633763346462313930383636636633316361363831373736616339393165222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235333838373961663638373934343136626135643934343264666230653932666630373161393637343731653964613231316562313664303733623334663131227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2265373335636664306138613534363931343437643063623630383665366262633864626466323137356431313665316139626366363234316663373063393436227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226f6c64227d", + "7b22727376223a312c2276616c223a5b312c325d7d" + ] + }, + "records": [ + [ + "state/x", + "old" + ], + [ + "state/y", + [ + 1, + 2 + ] + ] + ] + }, + { + "prefix": "é", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "é", + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "values": [ + "7b22727376223a312c2276616c223a22c3a9227d" + ] + }, + "records": [ + [ + "é", + "é" + ] + ] + }, + { + "prefix": "zz", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "zz", + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + }, + { + "prefix": "x", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "prefix": "x", + "nodes": [ + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + }, + { + "prefix": "", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "", + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2235643232636333316166373262343365646439383937313062306532303566343135313731313333363264303032313766633465663661306161663338616232222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2230613331653765653936303835383837306564373365363732623931316565663764303631656336363238646530636366636338343435373230636163333335227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235333838373961663638373934343136626135643934343264666230653932666630373161393637343731653964613231316562313664303733623334663131227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236383132643736356331376565373730343931353931623337633439366530653264343632633132643834333766323462333463626465313539633037633235227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238626138343432323735393662656538353831376436363833386438653034653963656230643436623766646337393232353234633165633539663339366133227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2262373331313832313335376466333964396663616635353966663262666638373062313635363933626361396665316238386661623332623935373735323437227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263393933333233336639646464616236663934363062636161366666626565653734383766376531316535333934636330353863616236636163633266623261227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2265343636646235346663626533663663613663633932643233383464316664346664333232373935336531396533373635653933343361633663623539613838227d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2263363061636538653238616363336437386566623533646632393063326335666439363063363833626265356164646336613463316639376166643636356537227d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2232306662646531306239386663633362396564366363653166316637616365663465343066356462636164633465353235396165613537366131333961393662227d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226e6577227d", + "7b22727376223a312c2276616c223a22c3a9227d", + "7b22727376223a312c2276616c223a5b312c325d7d", + "7b22727376223a312c2276616c223a6e756c6c7d", + "7b22727376223a312c2276616c223a747275657d", + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a367d7d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "log/6", + { + "event": 6 + } + ], + [ + "state/x", + "new" + ], + [ + "state/y", + [ + 1, + 2 + ] + ], + [ + "users/alice", + { + "n": "Alice" + } + ], + [ + "zz", + true + ], + [ + "é", + "é" + ], + [ + "😀", + null + ] + ] + }, + { + "prefix": "log/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "log/", + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2230613331653765653936303835383837306564373365363732623931316565663764303631656336363238646530636366636338343435373230636163333335227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236383132643736356331376565373730343931353931623337633439366530653264343632633132643834333766323462333463626465313539633037633235227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2262373331313832313335376466333964396663616635353966663262666638373062313635363933626361396665316238386661623332623935373735323437227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263393933333233336639646464616236663934363062636161366666626565653734383766376531316535333934636330353863616236636163633266623261227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2265343636646235346663626533663663613663633932643233383464316664346664333232373935336531396533373635653933343361633663623539613838227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a367d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "log/6", + { + "event": 6 + } + ] + ] + }, + { + "prefix": "users/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "users/", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "users/al", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "users/al", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "state/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "state/", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2235643232636333316166373262343365646439383937313062306532303566343135313731313333363264303032313766633465663661306161663338616232222c223739223a2236356435646466626364653363646335633564663533613663306137356235643834646439346165643965653261626131633137343564363634333966343230227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235333838373961663638373934343136626135643934343264666230653932666630373161393637343731653964613231316562313664303733623334663131227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238626138343432323735393662656538353831376436363833386438653034653963656230643436623766646337393232353234633165633539663339366133227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226e6577227d", + "7b22727376223a312c2276616c223a5b312c325d7d" + ] + }, + "records": [ + [ + "state/x", + "new" + ], + [ + "state/y", + [ + 1, + 2 + ] + ] + ] + }, + { + "prefix": "é", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "é", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2266653862643536303133383036356664636461343037353034316264316630343935393263626666326165633762313939366336346164323463653637383039227d" + ], + "values": [ + "7b22727376223a312c2276616c223a22c3a9227d" + ] + }, + "records": [ + [ + "é", + "é" + ] + ] + }, + { + "prefix": "zz", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "zz", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2263363061636538653238616363336437386566623533646632393063326335666439363063363833626265356164646336613463316639376166643636356537227d" + ], + "values": [ + "7b22727376223a312c2276616c223a747275657d" + ] + }, + "records": [ + [ + "zz", + true + ] + ] + }, + { + "prefix": "x", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "x", + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + } + ], + "extensions": [ + { + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "log/" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "sha256", + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "log/" + ], + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "proven": [ + "log/" + ] + }, + { + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "log/", + "é", + "😀" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "sha256", + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "log/", + "é", + "😀" + ], + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "proven": [ + "log/", + "é", + "😀" + ] + }, + { + "base": null, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "sha256", + "base": null, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "nodes": [] + }, + "proven": [ + "" + ] + }, + { + "base": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "sha256", + "base": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "nodes": [] + }, + "proven": [ + "" + ] + }, + { + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "extends": false, + "error": "'b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786' does not extend '7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f' under '': 'state/x' is changed in it" + }, + { + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "state/" + ], + "extends": false, + "error": "'b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786' does not extend '7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f' under 'state/': 'state/x' is changed in it" + }, + { + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "users/" + ], + "extends": false, + "error": "'b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786' does not extend '7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f' under 'users/': 'users/al' is absent from it" + } + ], + "refused": [ + { + "kind": "key", + "name": "a node byte changed", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c00", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + { + "kind": "key", + "name": "the value changed", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a333030" + }, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + { + "kind": "key", + "name": "claims absence", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": null + }, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + { + "kind": "key", + "name": "about another root", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238633764616338643132323535363239626535313333336364303965303437656435373339613437643761353162356166323535663830653638323964626330227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f" + }, + { + "kind": "key", + "name": "the last node dropped", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + { + "kind": "prefix", + "name": "a node missing", + "envelope": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "sha256", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefix": "users/", + "nodes": [ + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2232613737396132356663613365633261646566376632313865643039666365613135323239313832313262356463623839633565666636633661646161666366227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + }, + { + "kind": "extension", + "name": "claims a wider prefix", + "envelope": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "sha256", + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786", + "prefixes": [ + "" + ], + "nodes": [ + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862222c223336223a2231376339663662636463373364616430383934366563353063653433326333613530343366323038316232323636383664373733306331376334646462396636227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2230366338396638666265356163396436353464373630393031633930653961373830383465376634303661616465663633333539373866316362333365316332222c223332223a2238303966343430313362323366323938393065636464383961346236366331613931356465326533373063396338336233623566383230333462353738636432222c223333223a2266643162356666373565363038346463633739326463303866666634613231656431643731393538313636336131333131323165613634653232646432356135222c223334223a2232303262303937323931343933343932613664346331366532333161613630333063376662366438383436636464383161643634303863656334656165636264222c223335223a2239626235346133313662393231346437306138333838663838363337636165313264336363353330623763383039363965666236356430626566393963343862227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2237386637613836353034326262646139353665343434373864393463303734323939323431656161633563653035366133653735306136356238336662386362222c223733223a2235616461306165393539353764656566383062366564333261383037373435376530646336616463383732626137633734363236333439613661316631663666222c223735223a2232633135653164366531303039353530653931366335356431623037353135626531326633373161346535643030386463613433383039633533306562383864222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2266656232633233333632306164333638323263346237616364346139626333386239336461366630623537326334633763396537313139653836306462666232222c223733223a2234353130333634313561313966303737356331623638626634386666633761363633663430393133346662306265623539303565333234343662306462306362222c223735223a2234653934656436356432343662623831396534633134666265316361656433356363303130366532616135313134396632313963656263363363316130356564222c223761223a2232626331393038643761643739353365336234643232393638343635366538396432633738376163623031353962343664383835623766326264356332353566222c226333223a2231656332343330326165303464643033343333663037386639333631363333626661393730663364313338643132613634323731303937633433626339623535222c226630223a2231303166663464313661316433626662333933623866353738623135386137643730303061643432346564656436326338646437623466613264383434613761227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "base": "7a9b8b34ad0ea7cee9567e43a7dd6eb870673c8081ae07240fdeffb9dff19a8f", + "root": "b48efdba30412778238abb5a5c90ed9e7141a017395a6e748229dc86b2173786" + } + ] + }, + { + "addressing": "swarm", + "first": [ + { + "op": "put", + "key": "log/1", + "value": { + "event": 1 + } + }, + { + "op": "put", + "key": "log/2", + "value": { + "event": 2 + } + }, + { + "op": "put", + "key": "log/3", + "value": { + "event": 3 + } + }, + { + "op": "put", + "key": "log/4", + "value": { + "event": 4 + } + }, + { + "op": "put", + "key": "log/5", + "value": { + "event": 5 + } + }, + { + "op": "put", + "key": "state/x", + "value": "old" + }, + { + "op": "put", + "key": "state/y", + "value": [ + 1, + 2 + ] + }, + { + "op": "put", + "key": "é", + "value": "é" + }, + { + "op": "put", + "key": "😀", + "value": null + }, + { + "op": "put", + "key": "users/alice", + "value": { + "n": "Alice" + } + }, + { + "op": "put", + "key": "users/al", + "value": 0.5 + }, + { + "op": "commit" + } + ], + "second": [ + { + "op": "put", + "key": "log/6", + "value": { + "event": 6 + } + }, + { + "op": "put", + "key": "state/x", + "value": "new" + }, + { + "op": "delete", + "key": "users/al" + }, + { + "op": "put", + "key": "zz", + "value": true + }, + { + "op": "commit" + } + ], + "roots": { + "a": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "b": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + "empty": { + "key": "anything", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": null, + "key": "anything", + "present": false, + "nodes": [], + "value": null + } + }, + "keys": [ + { + "key": "log/3", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "present": true, + "value": { + "event": 3 + } + }, + { + "key": "state/x", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "state/x", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2231663564376334643936306461333234663762663534343662356339363436383734353261363762633531376238336137646331663166643863373066336431222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238656437343537333961303632653539333830613965383661343939333961373934363231386562376636323265653431353036633764356436633363373962227d" + ], + "value": "7b22727376223a312c2276616c223a226f6c64227d" + }, + "present": true, + "value": "old" + }, + { + "key": "users/alice", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "users/alice", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + }, + "present": true, + "value": { + "n": "Alice" + } + }, + { + "key": "users/al", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "users/al", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d" + ], + "value": "7b22727376223a312c2276616c223a302e357d" + }, + "present": true, + "value": 0.5 + }, + { + "key": "users/a", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "users/a", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/alicex", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "users/alicex", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "é", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "é", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "value": "7b22727376223a312c2276616c223a22c3a9227d" + }, + "present": true, + "value": "é" + }, + { + "key": "😀", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "😀", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2231613936623031386233653763656661633961646438613763333833616538643762643637623732336164343838306133656163313365613330363563303561227d" + ], + "value": "7b22727376223a312c2276616c223a6e756c6c7d" + }, + "present": true, + "value": null + }, + { + "key": "nope", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "nope", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "log/", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "zzz", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "key": "zzz", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/3", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "present": true, + "value": { + "event": 3 + } + }, + { + "key": "state/x", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "state/x", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2234643932316430316330386537663537636466623362316434633736626164633361623439656237613063623562663161313636386233383865623037343661222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233373038386231663865623938636435353366636565656532336262633034626363326366363566643033306431366534313435666566383930646565326431227d" + ], + "value": "7b22727376223a312c2276616c223a226e6577227d" + }, + "present": true, + "value": "new" + }, + { + "key": "users/alice", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "users/alice", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + }, + "present": true, + "value": { + "n": "Alice" + } + }, + { + "key": "users/al", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "users/al", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/a", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "users/a", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "users/alicex", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "users/alicex", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "é", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "é", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "value": "7b22727376223a312c2276616c223a22c3a9227d" + }, + "present": true, + "value": "é" + }, + { + "key": "😀", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "😀", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2231613936623031386233653763656661633961646438613763333833616538643762643637623732336164343838306133656163313365613330363563303561227d" + ], + "value": "7b22727376223a312c2276616c223a6e756c6c7d" + }, + "present": true, + "value": null + }, + { + "key": "nope", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "nope", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "log/", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": null + }, + "present": false, + "value": null + }, + { + "key": "zzz", + "proof": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "zzz", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2230383835623730376366326632366463383237383763623163616333373436646335376461643435323137656439643762386338633635633133396562636163227d" + ], + "value": null + }, + "present": false, + "value": null + } + ], + "prefixes": [ + { + "prefix": "", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "", + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2231663564376334643936306461333234663762663534343662356339363436383734353261363762633531376238336137646331663166643863373066336431222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231373463363865373562396365626635613566313064336630653533356438623765326238346433346438633331636138313532663737366136363135666430227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231653265323565333561363135656166626333653664353834336436313163346330666332626566616463666464373735333034643766396361393961306532227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235613166373964643833316138623135316534343730356366626564383834313437313836613662343931373130613065643236653732626162616533396334227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236366532346338303234343362653736343330326635343136386362663437656438323234653762623561633364613439326365303437313834653833643462227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238656437343537333961303632653539333830613965383661343939333961373934363231386562376636323265653431353036633764356436633363373962227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263373163353534303563353431333635383632653130656565323161373562323739333230653838303565366335616332616332303230376335656265373930227d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2231613936623031386233653763656661633961646438613763333833616538643762643637623732336164343838306133656163313365613330363563303561227d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226f6c64227d", + "7b22727376223a312c2276616c223a22c3a9227d", + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a5b312c325d7d", + "7b22727376223a312c2276616c223a6e756c6c7d", + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "state/x", + "old" + ], + [ + "state/y", + [ + 1, + 2 + ] + ], + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ], + [ + "é", + "é" + ], + [ + "😀", + null + ] + ] + }, + { + "prefix": "log/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "log/", + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231373463363865373562396365626635613566313064336630653533356438623765326238346433346438633331636138313532663737366136363135666430227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235613166373964643833316138623135316534343730356366626564383834313437313836613662343931373130613065643236653732626162616533396334227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236366532346338303234343362653736343330326635343136386362663437656438323234653762623561633364613439326365303437313834653833643462227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263373163353534303563353431333635383632653130656565323161373562323739333230653838303565366335616332616332303230376335656265373930227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ] + ] + }, + { + "prefix": "users/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "users/", + "nodes": [ + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "values": [ + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "users/al", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "users/al", + "nodes": [ + "7b2263223a7b223639223a2233306338616239383837393334306137643062346537633336383138643631643665366135326633313562336463383963396335613234303634616562313262227d2c2270223a223733363537323733326636313663222c22746e223a312c2276223a2232636364353430323935646632323636376663363036386232376534313665393239323766316434616130346335303233643335366164616439363066393031227d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "values": [ + "7b22727376223a312c2276616c223a302e357d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/al", + 0.5 + ], + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "state/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "state/", + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2231663564376334643936306461333234663762663534343662356339363436383734353261363762633531376238336137646331663166643863373066336431222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231653265323565333561363135656166626333653664353834336436313163346330666332626566616463666464373735333034643766396361393961306532227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2238656437343537333961303632653539333830613965383661343939333961373934363231386562376636323265653431353036633764356436633363373962227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226f6c64227d", + "7b22727376223a312c2276616c223a5b312c325d7d" + ] + }, + "records": [ + [ + "state/x", + "old" + ], + [ + "state/y", + [ + 1, + 2 + ] + ] + ] + }, + { + "prefix": "é", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "é", + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "values": [ + "7b22727376223a312c2276616c223a22c3a9227d" + ] + }, + "records": [ + [ + "é", + "é" + ] + ] + }, + { + "prefix": "zz", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "zz", + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + }, + { + "prefix": "x", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "prefix": "x", + "nodes": [ + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + }, + { + "prefix": "", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "", + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2234643932316430316330386537663537636466623362316434633736626164633361623439656237613063623562663161313636386233383865623037343661222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231373463363865373562396365626635613566313064336630653533356438623765326238346433346438633331636138313532663737366136363135666430227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231653265323565333561363135656166626333653664353834336436313163346330666332626566616463666464373735333034643766396361393961306532227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233373038386231663865623938636435353366636565656532336262633034626363326366363566643033306431366534313435666566383930646565326431227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235613166373964643833316138623135316534343730356366626564383834313437313836613662343931373130613065643236653732626162616533396334227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236366532346338303234343362653736343330326635343136386362663437656438323234653762623561633364613439326365303437313834653833643462227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236613731346539353030386534333038616437623834323331323238643062363366363535666433376362373133303634393033646561333261313734633935227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263373163353534303563353431333635383632653130656565323161373562323739333230653838303565366335616332616332303230376335656265373930227d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2230383835623730376366326632366463383237383763623163616333373436646335376461643435323137656439643762386338633635633133396562636163227d", + "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2231613936623031386233653763656661633961646438613763333833616538643762643637623732336164343838306133656163313365613330363563303561227d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226e6577227d", + "7b22727376223a312c2276616c223a22c3a9227d", + "7b22727376223a312c2276616c223a5b312c325d7d", + "7b22727376223a312c2276616c223a6e756c6c7d", + "7b22727376223a312c2276616c223a747275657d", + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a367d7d", + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "log/6", + { + "event": 6 + } + ], + [ + "state/x", + "new" + ], + [ + "state/y", + [ + 1, + 2 + ] + ], + [ + "users/alice", + { + "n": "Alice" + } + ], + [ + "zz", + true + ], + [ + "é", + "é" + ], + [ + "😀", + null + ] + ] + }, + { + "prefix": "log/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "log/", + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231373463363865373562396365626635613566313064336630653533356438623765326238346433346438633331636138313532663737366136363135666430227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2235613166373964643833316138623135316534343730356366626564383834313437313836613662343931373130613065643236653732626162616533396334227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236366532346338303234343362653736343330326635343136386362663437656438323234653762623561633364613439326365303437313834653833643462227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2236613731346539353030386534333038616437623834323331323238643062363366363535666433376362373133303634393033646561333261313734633935227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2263373163353534303563353431333635383632653130656565323161373562323739333230653838303565366335616332616332303230376335656265373930227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226576656e74223a317d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a327d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a337d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a347d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a357d7d", + "7b22727376223a312c2276616c223a7b226576656e74223a367d7d" + ] + }, + "records": [ + [ + "log/1", + { + "event": 1 + } + ], + [ + "log/2", + { + "event": 2 + } + ], + [ + "log/3", + { + "event": 3 + } + ], + [ + "log/4", + { + "event": 4 + } + ], + [ + "log/5", + { + "event": 5 + } + ], + [ + "log/6", + { + "event": 6 + } + ] + ] + }, + { + "prefix": "users/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "users/", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "users/al", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "users/al", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "records": [ + [ + "users/alice", + { + "n": "Alice" + } + ] + ] + }, + { + "prefix": "state/", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "state/", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223738223a2234643932316430316330386537663537636466623362316434633736626164633361623439656237613063623562663161313636386233383865623037343661222c223739223a2263353964386263383036356563366633663630643435383134666465663735333931613630663231616433343538383866393735373337393539336236366134227d2c2270223a2237343631373436353266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2231653265323565333561363135656166626333653664353834336436313163346330666332626566616463666464373735333034643766396361393961306532227d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233373038386231663865623938636435353366636565656532336262633034626363326366363566643033306431366534313435666566383930646565326431227d" + ], + "values": [ + "7b22727376223a312c2276616c223a226e6577227d", + "7b22727376223a312c2276616c223a5b312c325d7d" + ] + }, + "records": [ + [ + "state/x", + "new" + ], + [ + "state/y", + [ + 1, + 2 + ] + ] + ] + }, + { + "prefix": "é", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "é", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2231633638303138653464323430663431633430386563363265353531653136396264383236343938656437616239656232663634363236626233306530623639227d" + ], + "values": [ + "7b22727376223a312c2276616c223a22c3a9227d" + ] + }, + "records": [ + [ + "é", + "é" + ] + ] + }, + { + "prefix": "zz", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "zz", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a223761222c22746e223a312c2276223a2230383835623730376366326632366463383237383763623163616333373436646335376461643435323137656439643762386338633635633133396562636163227d" + ], + "values": [ + "7b22727376223a312c2276616c223a747275657d" + ] + }, + "records": [ + [ + "zz", + true + ] + ] + }, + { + "prefix": "x", + "proof": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "x", + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ], + "values": [] + }, + "records": [] + } + ], + "extensions": [ + { + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "log/" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "swarm", + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "log/" + ], + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "proven": [ + "log/" + ] + }, + { + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "log/", + "é", + "😀" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "swarm", + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "log/", + "é", + "😀" + ], + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "proven": [ + "log/", + "é", + "😀" + ] + }, + { + "base": null, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "swarm", + "base": null, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "nodes": [] + }, + "proven": [ + "" + ] + }, + { + "base": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "extends": true, + "proof": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "swarm", + "base": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "nodes": [] + }, + "proven": [ + "" + ] + }, + { + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "extends": false, + "error": "'109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d' does not extend '9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c' under '': 'state/x' is changed in it" + }, + { + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "state/" + ], + "extends": false, + "error": "'109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d' does not extend '9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c' under 'state/': 'state/x' is changed in it" + }, + { + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "users/" + ], + "extends": false, + "error": "'109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d' does not extend '9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c' under 'users/': 'users/al' is absent from it" + } + ], + "refused": [ + { + "kind": "key", + "name": "a node byte changed", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c00", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + { + "kind": "key", + "name": "the value changed", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a333030" + }, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + { + "kind": "key", + "name": "claims absence", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": false, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": null + }, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + { + "kind": "key", + "name": "about another root", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2233356561353538653539653337323231663530366438363263393666356263663261313933306366626135656337393266666636636132363333616337346632227d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c" + }, + { + "kind": "key", + "name": "the last node dropped", + "envelope": { + "format": "recordstore-trie-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "key": "log/3", + "present": true, + "nodes": [ + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d" + ], + "value": "7b22727376223a312c2276616c223a7b226576656e74223a337d7d" + }, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + { + "kind": "prefix", + "name": "a node missing", + "envelope": { + "format": "recordstore-prefix-proof", + "version": 1, + "addressing": "swarm", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefix": "users/", + "nodes": [ + "7b2263223a7b7d2c2270223a223733363537323733326636313663363936333635222c22746e223a312c2276223a2234643163396361313663656336396339383935343562313836616238303430323133343335393361376330326462333936646361333661626130326334323936227d" + ], + "values": [ + "7b22727376223a312c2276616c223a7b226e223a22416c696365227d7d" + ] + }, + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + }, + { + "kind": "extension", + "name": "claims a wider prefix", + "envelope": { + "format": "recordstore-extension-proof", + "version": 1, + "addressing": "swarm", + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d", + "prefixes": [ + "" + ], + "nodes": [ + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662222c223336223a2239616363623766393737646339326631363135303833643736306330386230656133633035663932363937323432353661326661656165613464356262616533227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223331223a2237663632656631636564613536326332386661663064613331633061303838343964656566333533633733336439653838363061356266323136656664306533222c223332223a2237333961343134333633383433306465643639653037393037636634643330343331353362623039303163333739663834613364333839323934373533396431222c223333223a2235393634366331326637663334626239636438336562363136313762613063383837383534333435646561613734326561346434616331636530313138346538222c223334223a2232626630393463353235613735643562306334633638326237343163303333313231666239636239373265323439313832343330636233653039616566353966222c223335223a2265306634663436323364333861343261366234326132626636386535313336386334653334323637623863346262303235373031333461626465663465323662227d2c2270223a22366636373266222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2232373066343366313731393263323263343734336339633134643666643832636537363763626635386338393137666464316661613366313866326336353536222c223733223a2266383461643632313364363366383434343837353631616661313634623562363638633761396631626138383133643933616566396466376662343539306138222c223735223a2237313862343836343739373335646536646634373137663061383839346135663039353461326638313963393239303631616562373439343266323238653430222c223761223a2266333862343663626264383263663333623639383837346533393339313339353639313338383961376263373635356164356136316336626633386532346538222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "7b2263223a7b223663223a2238346332366531366531336138313062316132363334313166336663663333663034636438356134383433386635313737373138393138316238343031386530222c223733223a2238666130643438373038343331623463303535316166646431306133653862333566656663363139356363323365643233356665373736393762363637376239222c223735223a2231333766303836616539333261623662373538313036323135326166313762363664663536663963326162653762623238393761313364636365323965353838222c226333223a2233393036353332383561616532343366386134323735306430323736656466333361313335663832656133313838613034636331376638386564353666343039222c226630223a2231316637633663353366633861333839636639366535363735623738633031666364363837356430323230613166333661313633616331343566393236303831227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d" + ] + }, + "base": "9718efc41d969461e9fde2bc94929cb02c85e07634c08b38e7f51a9ff2b9a99c", + "root": "109c1586e2735395e5f222e50c51708e3a71ab945ce4679eb1e89c08ccd6123d" + } + ] + } + ] +} diff --git a/js/test/fixtures/stores.json b/js/test/fixtures/stores.json new file mode 100644 index 0000000..3a7a8ca --- /dev/null +++ b/js/test/fixtures/stores.json @@ -0,0 +1,10979 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "cases": [ + { + "name": "scripted/sha256", + "addressing": "sha256", + "steps": [ + { + "op": "put", + "key": "users/alice", + "value": { + "name": "Alice", + "role": "admin" + } + }, + { + "op": "put", + "key": "users/bob", + "value": { + "name": "Bob" + } + }, + { + "op": "put", + "key": "users/al", + "value": "a proper prefix of alice" + }, + { + "op": "put", + "key": "u", + "value": 1 + }, + { + "op": "put", + "key": "userz", + "value": [ + 1, + 2, + 3 + ] + }, + { + "op": "commit", + "root": "a38759063ca99e0bd84acc1b6267aff9e4802bccc9a5b808ef3dbc0da96d7026" + }, + { + "op": "delete", + "key": "users/al", + "existed": true + }, + { + "op": "commit", + "root": "98c2151ebf6f2bec3265a169dd27e4d49e6c9d981f2c7db12bd5dc37c60f03f6" + }, + { + "op": "put", + "key": "users/alice", + "value": { + "name": "Alice", + "role": "admin" + } + }, + { + "op": "commit", + "root": "98c2151ebf6f2bec3265a169dd27e4d49e6c9d981f2c7db12bd5dc37c60f03f6" + }, + { + "op": "put", + "key": "é", + "value": "two bytes" + }, + { + "op": "put", + "key": "😀", + "value": "four bytes" + }, + { + "op": "put", + "key": "￿", + "value": "three bytes, sorts before the emoji" + }, + { + "op": "put", + "key": "a/b/c", + "value": null + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "never-was", + "existed": false + }, + { + "op": "commit", + "root": "4b14fa9c584f0bec2a2484c6fae26dc7efb35a7bc0c01eea568d8593b226dda6" + }, + { + "op": "put", + "key": "tmp", + "value": "staged then deleted" + }, + { + "op": "delete", + "key": "tmp", + "existed": true + }, + { + "op": "put", + "key": "users/bob", + "value": { + "name": "Bob", + "age": 0.5 + } + }, + { + "op": "commit", + "root": "a35fceeecd9a0f1792d90f8b41a0e1ffab5c9b125f6a2ad6f4d1a55128479276" + }, + { + "op": "delete", + "key": "users/alice", + "existed": true + }, + { + "op": "delete", + "key": "users/bob", + "existed": true + }, + { + "op": "delete", + "key": "userz", + "existed": true + }, + { + "op": "commit", + "root": "3a96f2f949ab2fde23ee9c0f529bd371393e5d96a240ef694874d16917aac5fc" + } + ], + "items": [ + [ + "a/b/c", + null + ], + [ + "é", + "two bytes" + ], + [ + "￿", + "three bytes, sorts before the emoji" + ], + [ + "😀", + "four bytes" + ] + ], + "blobs": { + "0375b540e87ae4afba9935315fe44e7a569569828b8741182165d92efb70a037": "7b2263223a7b223733223a2262613962346364663833626137393432323264623838646363653730323937656631383463666263623435663766643834643832653838663635303466623063222c223761223a2234646638666165386430316361626135323937376634343835363233383135656538363064346361393865306632653030663964616430376337303533623561227d2c2270223a22373336353732222c22746e223a312c2276223a6e756c6c7d", + "08e62e822c952f0d654ea91e43adf8de5b654fa32d7a4e367689df2505d6113e": "7b2263223a7b223631223a2239656663303665353762346562373162373334336461323133386463376261346532643166323133306138323063623561396663623238366539373136633362222c223632223a2239333666633932623236363539646462373837616337366662306262373163653437346332623139356561643164313663323061623635376263373465366238227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "0a1e6cd41cc66fe2a711a7ad53a7d4f856aa5b3384d6087cd83327216a1a5fc9": "7b2263223a7b7d2c2270223a223266363232663633222c22746e223a312c2276223a2232306662646531306239386663633362396564366363653166316637616365663465343066356462636164633465353235396165613537366131333961393662227d", + "0ba10f3d8ff85286020a7a876655ab07217d6b22663b74ee08c831ea55c381de": "7b2263223a7b223733223a2261393939326431326565613266656335656261373035643863616534356334343933393639653365333733643933626262626339376337363164376233313436222c223761223a2234646638666165386430316361626135323937376634343835363233383135656538363064346361393865306632653030663964616430376337303533623561227d2c2270223a2236353732222c22746e223a312c2276223a6e756c6c7d", + "109986fc4ee45c8a9ebe7921452423461e82aa144adefe704819e9a2b8944125": "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2237316336653462656461333461383934326234636265383064636134623064366464333333613964346163326239653666356264666465353065336466393639227d", + "20fbde10b98fcc3b9ed6cce1f1f7acef4e40f5dbcadc4e5259aea576a139a96b": "7b22727376223a312c2276616c223a6e756c6c7d", + "2b8f14177efeed39c7929bd1c50d89e817fb75084da2c0cace2b10daf1df6fdd": "7b2263223a7b223733223a2230386536326538323263393532663064363534656139316534336164663864653562363534666133326437613465333637363839646632353035643631313365222c223761223a2234646638666165386430316361626135323937376634343835363233383135656538363064346361393865306632653030663964616430376337303533623561227d2c2270223a2236353732222c22746e223a312c2276223a6e756c6c7d", + "3a96f2f949ab2fde23ee9c0f529bd371393e5d96a240ef694874d16917aac5fc": "7b2263223a7b223631223a2230613165366364343163633636666532613731316137616435336137643466383536616135623333383464363038376364383333323732313661316135666339222c226333223a2264646231356536306363323330373638383536306364396430333730346161376263376137363130656132623734373136313335363665653463373662313933222c226566223a2265333234633434326137666433376137333031636334643333646161383534393331383162333066373435346562313964366363653163366438396339353539222c226630223a2231303939383666633465653435633861396562653739323134353234323334363165383261613134346164656665373034383139653961326238393434313235227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "3e7a72fb8a26e9f35d6a2f4519f26f21cad2c79bbf634682442d5d9d56039e87": "7b2263223a7b7d2c2270223a2236663632222c22746e223a312c2276223a2263313562633163373535366431393062363638363863623131383938646461303861616630396536623639386239353963613435376165623361336237663932227d", + "442bd272d7ec9c6b0ade5fa78d7e91296bb70564138592c79c3eb1c5cd7f78ad": "7b2263223a7b223639223a2261636434316434303739623962646230396461623136386466653966363136366237313837313334366463303234356561333561636136313332326562653037227d2c2270223a223663222c22746e223a312c2276223a2265653436336234363661366334373637393234643334326461643863373064323039643739626332333139356565616465666664653161633531643537613631227d", + "4b14fa9c584f0bec2a2484c6fae26dc7efb35a7bc0c01eea568d8593b226dda6": "7b2263223a7b223631223a2230613165366364343163633636666532613731316137616435336137643466383536616135623333383464363038376364383333323732313661316135666339222c223735223a2261663862633765333630633133363635313930323566333338393434343964663532643631306164653162653830633939623337663338653866356562343036222c226333223a2264646231356536306363323330373638383536306364396430333730346161376263376137363130656132623734373136313335363665653463373662313933222c226566223a2265333234633434326137666433376137333031636334643333646161383534393331383162333066373435346562313964366363653163366438396339353539222c226630223a2231303939383666633465653435633861396562653739323134353234323334363165383261613134346164656665373034383139653961326238393434313235227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "4df8fae8d01caba52977f4485623815ee860d4ca98e0f2e00f9dad07c7053b5a": "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2264663061666665313634343338363035616661646561333639303339383465316366633531336534663038333865653039646162363939633239353836326339227d", + "532ea093118282e16cb7d431f295e59109eb733661e539978cffd99b39e7b533": "7b22727376223a312c2276616c223a317d", + "71c6e4beda34a8942b4cbe80dca4b0d6dd333a9d4ac2b9e6f5bdfde50e3df969": "7b22727376223a312c2276616c223a22666f7572206279746573227d", + "8274c8dccf1afdda59ca0efa35cf974e49e3c5d7707a40ce9e14fcc11138f425": "7b22727376223a312c2276616c223a2274776f206279746573227d", + "936fc92b26659ddb787ac76fb0bb71ce474c2b195ead1d16c20ab657bc74e6b8": "7b2263223a7b7d2c2270223a2236663632222c22746e223a312c2276223a2263386431633536353664633032626635613262616666313039396431386338656435376330383061346138316366646532663161626239396139653936633165227d", + "98c2151ebf6f2bec3265a169dd27e4d49e6c9d981f2c7db12bd5dc37c60f03f6": "7b2263223a7b223733223a2232623866313431373765666565643339633739323962643163353064383965383137666237353038346461326330636163653262313064616631646636666464227d2c2270223a223735222c22746e223a312c2276223a2235333265613039333131383238326531366362376434333166323935653539313039656237333336363165353339393738636666643939623339653762353333227d", + "9efc06e57b4eb71b7343da2138dc7ba4e2d1f2130a820cb5a9fcb286e9716c3b": "7b2263223a7b7d2c2270223a223663363936333635222c22746e223a312c2276223a2265336662353131373464663631613963363339653165363235333132613462333930393062633330373932636234613664316461303162306630636664613235227d", + "a35fceeecd9a0f1792d90f8b41a0e1ffab5c9b125f6a2ad6f4d1a55128479276": "7b2263223a7b223631223a2230613165366364343163633636666532613731316137616435336137643466383536616135623333383464363038376364383333323732313661316135666339222c223735223a2230333735623534306538376165346166626139393335333135666534346537613536393536393832386238373431313832313635643932656662373061303337222c226333223a2264646231356536306363323330373638383536306364396430333730346161376263376137363130656132623734373136313335363665653463373662313933222c226566223a2265333234633434326137666433376137333031636334643333646161383534393331383162333066373435346562313964366363653163366438396339353539222c226630223a2231303939383666633465653435633861396562653739323134353234323334363165383261613134346164656665373034383139653961326238393434313235227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "a38759063ca99e0bd84acc1b6267aff9e4802bccc9a5b808ef3dbc0da96d7026": "7b2263223a7b223733223a2230626131306633643866663835323836303230613761383736363535616230373231376436623232363633623734656530386338333165613535633338316465227d2c2270223a223735222c22746e223a312c2276223a2235333265613039333131383238326531366362376434333166323935653539313039656237333336363165353339393738636666643939623339653762353333227d", + "a9992d12eea2fec5eba705d8cae45c4493969e3e373d93bbbbc97c761d7b3146": "7b2263223a7b223631223a2234343262643237326437656339633662306164653566613738643765393132393662623730353634313338353932633739633365623163356364376637386164222c223632223a2239333666633932623236363539646462373837616337366662306262373163653437346332623139356561643164313663323061623635376263373465366238227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "acd41d4079b9bdb09dab168dfe9f6166b71871346dc0245ea35aca61322ebe07": "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2265336662353131373464663631613963363339653165363235333132613462333930393062633330373932636234613664316461303162306630636664613235227d", + "af8bc7e360c1366519025f33894449df52d610ade1be80c99b37f38e8f5eb406": "7b2263223a7b223733223a2230386536326538323263393532663064363534656139316534336164663864653562363534666133326437613465333637363839646632353035643631313365222c223761223a2234646638666165386430316361626135323937376634343835363233383135656538363064346361393865306632653030663964616430376337303533623561227d2c2270223a22373336353732222c22746e223a312c2276223a6e756c6c7d", + "ba9b4cdf83ba794222db88dcce70297ef184cfbcb45f7fd84d82e88f6504fb0c": "7b2263223a7b223631223a2239656663303665353762346562373162373334336461323133386463376261346532643166323133306138323063623561396663623238366539373136633362222c223632223a2233653761373266623861323665396633356436613266343531396632366632316361643263373962626636333436383234343264356439643536303339653837227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "c15bc1c7556d190b66868cb11898dda08aaf09e6b698b959ca457aeb3a3b7f92": "7b22727376223a312c2276616c223a7b22616765223a302e352c226e616d65223a22426f62227d7d", + "c8d1c5656dc02bf5a2baff1099d18c8ed57c080a4a81cfde2f1abb99a9e96c1e": "7b22727376223a312c2276616c223a7b226e616d65223a22426f62227d7d", + "ddb15e60cc2307688560cd9d03704aa7bc7a7610ea2b7471613566ee4c76b193": "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2238323734633864636366316166646461353963613065666133356366393734653439653363356437373037613430636539653134666363313131333866343235227d", + "df0affe164438605afadea36903984e1cfc513e4f0838ee09dab699c295862c9": "7b22727376223a312c2276616c223a5b312c322c335d7d", + "e02aaf34983040dabcf56c215587d7cb89994dbf1c393d98b8e01407970087d8": "7b22727376223a312c2276616c223a2274687265652062797465732c20736f727473206265666f72652074686520656d6f6a69227d", + "e324c442a7fd37a7301cc4d33daa85493181b30f7454eb19d6cce1c6d89c9559": "7b2263223a7b7d2c2270223a2262666266222c22746e223a312c2276223a2265303261616633343938333034306461626366353663323135353837643763623839393934646266316333393364393862386530313430373937303038376438227d", + "e3fb51174df61a9c639e1e625312a4b39090bc30792cb4a6d1da01b0f0cfda25": "7b22727376223a312c2276616c223a7b226e616d65223a22416c696365222c22726f6c65223a2261646d696e227d7d", + "ee463b466a6c4767924d342dad8c70d209d79bc23195eeadeffde1ac51d57a61": "7b22727376223a312c2276616c223a22612070726f70657220707265666978206f6620616c696365227d" + } + }, + { + "name": "scripted/swarm", + "addressing": "swarm", + "steps": [ + { + "op": "put", + "key": "users/alice", + "value": { + "name": "Alice", + "role": "admin" + } + }, + { + "op": "put", + "key": "users/bob", + "value": { + "name": "Bob" + } + }, + { + "op": "put", + "key": "users/al", + "value": "a proper prefix of alice" + }, + { + "op": "put", + "key": "u", + "value": 1 + }, + { + "op": "put", + "key": "userz", + "value": [ + 1, + 2, + 3 + ] + }, + { + "op": "commit", + "root": "ed32291ea33f8f6b24e9d0fe8b6011e82ca4ec5e4c3564cdf2cc38f8b41bcc5d" + }, + { + "op": "delete", + "key": "users/al", + "existed": true + }, + { + "op": "commit", + "root": "361d6fe0c0712a6c9a3ba78010f1a1d0adaa17d4aed9857768cb96a131a53969" + }, + { + "op": "put", + "key": "users/alice", + "value": { + "name": "Alice", + "role": "admin" + } + }, + { + "op": "commit", + "root": "361d6fe0c0712a6c9a3ba78010f1a1d0adaa17d4aed9857768cb96a131a53969" + }, + { + "op": "put", + "key": "é", + "value": "two bytes" + }, + { + "op": "put", + "key": "😀", + "value": "four bytes" + }, + { + "op": "put", + "key": "￿", + "value": "three bytes, sorts before the emoji" + }, + { + "op": "put", + "key": "a/b/c", + "value": null + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "never-was", + "existed": false + }, + { + "op": "commit", + "root": "aa03a7c124e6029aa5e8c919532214187f0d8cff77ccd05185c8b0f6d5048c10" + }, + { + "op": "put", + "key": "tmp", + "value": "staged then deleted" + }, + { + "op": "delete", + "key": "tmp", + "existed": true + }, + { + "op": "put", + "key": "users/bob", + "value": { + "name": "Bob", + "age": 0.5 + } + }, + { + "op": "commit", + "root": "d02fbbe6d9be8d6a91d383d3668fc3753bd4f0c5a5abec219242f032b7ba0e3f" + }, + { + "op": "delete", + "key": "users/alice", + "existed": true + }, + { + "op": "delete", + "key": "users/bob", + "existed": true + }, + { + "op": "delete", + "key": "userz", + "existed": true + }, + { + "op": "commit", + "root": "1c875ddfe7852119d0a8e25ec0e5d0be616f811ff01ba73e6f4fb529d8211f86" + } + ], + "items": [ + [ + "a/b/c", + null + ], + [ + "é", + "two bytes" + ], + [ + "￿", + "three bytes, sorts before the emoji" + ], + [ + "😀", + "four bytes" + ] + ], + "blobs": { + "05b5d567f16c3c5e28807ce75d5c312fd34cd8682f87d3ca304a781766275bd7": "7b2263223a7b7d2c2270223a2236333635222c22746e223a312c2276223a2235356462333731666261346231363234356466636461313666653563633565653036356237363136323735373039626432383866376234636536333663396231227d", + "06698071c1052b018fbc0550819448526e9da9514b4fdf4baca998586a2a287a": "7b2263223a7b223733223a2262636230303732656363313437646262326563356563316338623839313463663763316437376239646238616431343166386538396130633664323738623138222c223761223a2231636331303261633538393437316361373665313535316364316233366135343431386461313636363866653334656332313331643639373031313839656639227d2c2270223a22373336353732222c22746e223a312c2276223a6e756c6c7d", + "0721f9178381ea0ede44b5b4bf63c69fd367f6e6f424644646c5f358b580db17": "7b22727376223a312c2276616c223a7b22616765223a302e352c226e616d65223a22426f62227d7d", + "0ce8f00f8394bd668f57d22a546e442bcc8e005d792dd7a1d57f37169239ade5": "7b22727376223a312c2276616c223a317d", + "0e27ffdc6908d04b79a1136c92aa0c38b3dc476e72d0fe36e50fa8d75160575e": "7b2263223a7b7d2c2270223a2236663632222c22746e223a312c2276223a2230373231663931373833383165613065646534346235623462663633633639666433363766366536663432343634343634366335663335386235383064623137227d", + "108db57d052f243c2d58f670ca19b60b1132461d68faa5e072b1a91db1be2617": "7b2263223a7b223733223a2232663866636536373734616463356264643230396638326563303235356238653930653130303234313233373339393065363463616230303135333531353463222c223761223a2231636331303261633538393437316361373665313535316364316233366135343431386461313636363866653334656332313331643639373031313839656639227d2c2270223a2236353732222c22746e223a312c2276223a6e756c6c7d", + "17596c4eea79c7ca2075b252c150f4849d0a1e749d82d28a5e4a37236823b24b": "7b2263223a7b223639223a2230356235643536376631366333633565323838303763653735643563333132666433346364383638326638376433636133303461373831373636323735626437227d2c2270223a223663222c22746e223a312c2276223a2265613531396363316533323930383131313632366365643235353936326430383232326334326330306264343935393862656637663731376331636561306163227d", + "1a96b018b3e7cefac9add8a7c383ae8d7bd67b723ad4880a3eac13ea3065c05a": "7b22727376223a312c2276616c223a6e756c6c7d", + "1c875ddfe7852119d0a8e25ec0e5d0be616f811ff01ba73e6f4fb529d8211f86": "7b2263223a7b223631223a2233363266386638383365313864346366306661653134613838323932396361336232396338343063343963616336666637616434623964303939333765613465222c226333223a2262343533653739393939393235343333333238626137333538633065393938666630313430623332386266623665666163663638656362353761393831313333222c226566223a2264663039613364373531353832643535386465616337396466373665633064356439363236353231303666613736373866613930353562376237626561316564222c226630223a2236636230666634643537313362313932383761626439383032333532343133646533616338343632626631653037393164656137393432386461303538653063227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "1cc102ac589471ca76e1551cd1b36a54418da16668fe34ec2131d69701189ef9": "7b2263223a7b7d2c2270223a22222c22746e223a312c2276223a2261633936393232613038353965316133333139343565613861626433316664373965643933633862333665366339366536353935316435646334393132623332227d", + "2f8fce6774adc5bdd209f82ec0255b8e90e1002412373990e64cab001535154c": "7b2263223a7b223631223a2235626534306361316364663430396639646236346562343465613063353266623030663864333532303936323739313136643061613131383966373339663934222c223632223a2236393137613361386531373062636665356131386566303263393065393765626266313562663835386337363763326561613561643865623531336362383030227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "361cf9760f0af3d3be81ef27ae8300c8cef7ead23727a6b77014a496c3474ae5": "7b22727376223a312c2276616c223a22666f7572206279746573227d", + "361d6fe0c0712a6c9a3ba78010f1a1d0adaa17d4aed9857768cb96a131a53969": "7b2263223a7b223733223a2231303864623537643035326632343363326435386636373063613139623630623131333234363164363866616135653037326231613931646231626532363137227d2c2270223a223735222c22746e223a312c2276223a2230636538663030663833393462643636386635376432326135343665343432626363386530303564373932646437613164353766333731363932333961646535227d", + "362f8f883e18d4cf0fae14a882929ca3b29c840c49cac6ff7ad4b9d09937ea4e": "7b2263223a7b7d2c2270223a223266363232663633222c22746e223a312c2276223a2231613936623031386233653763656661633961646438613763333833616538643762643637623732336164343838306133656163313365613330363563303561227d", + "49035f01f2d1909198278fa4108e5aa829d04737565d21d1dd3cf77715722a34": "7b2263223a7b223631223a2231373539366334656561373963376361323037356232353263313530663438343964306131653734396438326432386135653461333732333638323362323462222c223632223a2236393137613361386531373062636665356131386566303263393065393765626266313562663835386337363763326561613561643865623531336362383030227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "55db371fba4b16245dfcda16fe5cc5ee065b7616275709bd288f7b4ce636c9b1": "7b22727376223a312c2276616c223a7b226e616d65223a22416c696365222c22726f6c65223a2261646d696e227d7d", + "5be40ca1cdf409f9db64eb44ea0c52fb00f8d352096279116d0aa1189f739f94": "7b2263223a7b7d2c2270223a223663363936333635222c22746e223a312c2276223a2235356462333731666261346231363234356466636461313666653563633565653036356237363136323735373039626432383866376234636536333663396231227d", + "6917a3a8e170bcfe5a18ef02c90e97ebbf15bf858c767c2eaa5ad8eb513cb800": "7b2263223a7b7d2c2270223a2236663632222c22746e223a312c2276223a2262323930313663396232396337343335626138333463323835653665383761393136393765366330376537336430623136343864656634653165386165303165227d", + "6cb0ff4d5713b19287abd9802352413de3ac8462bf1e0791dea79428da058e0c": "7b2263223a7b7d2c2270223a22396639383830222c22746e223a312c2276223a2233363163663937363066306166336433626538316566323761653833303063386365663765616432333732376136623737303134613439366333343734616535227d", + "85e8cd8d7bfc91b0eecab242bbb15c60ff8940202701bc069f92cab2a471459d": "7b2263223a7b223733223a2234393033356630316632643139303931393832373866613431303865356161383239643034373337353635643231643164643363663737373135373232613334222c223761223a2231636331303261633538393437316361373665313535316364316233366135343431386461313636363866653334656332313331643639373031313839656639227d2c2270223a2236353732222c22746e223a312c2276223a6e756c6c7d", + "a1492bc0c8cce3faa685187ae430d1d2dfaf15cbbdcaeff0ae3df52bd3ba29f8": "7b2263223a7b223733223a2232663866636536373734616463356264643230396638326563303235356238653930653130303234313233373339393065363463616230303135333531353463222c223761223a2231636331303261633538393437316361373665313535316364316233366135343431386461313636363866653334656332313331643639373031313839656639227d2c2270223a22373336353732222c22746e223a312c2276223a6e756c6c7d", + "a4e595501cd6b69bb3f9799f2716ec3eae80a5843a512f3ae501f753932c27f5": "7b22727376223a312c2276616c223a2274687265652062797465732c20736f727473206265666f72652074686520656d6f6a69227d", + "aa03a7c124e6029aa5e8c919532214187f0d8cff77ccd05185c8b0f6d5048c10": "7b2263223a7b223631223a2233363266386638383365313864346366306661653134613838323932396361336232396338343063343963616336666637616434623964303939333765613465222c223735223a2261313439326263306338636365336661613638353138376165343330643164326466616631356362626463616566663061653364663532626433626132396638222c226333223a2262343533653739393939393235343333333238626137333538633065393938666630313430623332386266623665666163663638656362353761393831313333222c226566223a2264663039613364373531353832643535386465616337396466373665633064356439363236353231303666613736373866613930353562376237626561316564222c226630223a2236636230666634643537313362313932383761626439383032333532343133646533616338343632626631653037393164656137393432386461303538653063227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "ac96922a0859e1a331945ea8abd31fd79ed93c8b36e6c96e65951d5dc4912b32": "7b22727376223a312c2276616c223a5b312c322c335d7d", + "b29016c9b29c7435ba834c285e6e87a91697e6c07e73d0b1648def4e1e8ae01e": "7b22727376223a312c2276616c223a7b226e616d65223a22426f62227d7d", + "b453e79999925433328ba7358c0e998ff0140b328bfb6efacf68ecb57a981133": "7b2263223a7b7d2c2270223a226139222c22746e223a312c2276223a2262633537346462653635616562303233303363336665653733613933633534633665623265323563623031383530346264396166623031623232303962616536227d", + "bc574dbe65aeb02303c3fee73a93c54c6eb2e25cb018504bd9afb01b2209bae6": "7b22727376223a312c2276616c223a2274776f206279746573227d", + "bcb0072ecc147dbb2ec5ec1c8b8914cf7c1d77b9db8ad141f8e89a0c6d278b18": "7b2263223a7b223631223a2235626534306361316364663430396639646236346562343465613063353266623030663864333532303936323739313136643061613131383966373339663934222c223632223a2230653237666664633639303864303462373961313133366339326161306333386233646334373665373264306665333665353066613864373531363035373565227d2c2270223a223266222c22746e223a312c2276223a6e756c6c7d", + "d02fbbe6d9be8d6a91d383d3668fc3753bd4f0c5a5abec219242f032b7ba0e3f": "7b2263223a7b223631223a2233363266386638383365313864346366306661653134613838323932396361336232396338343063343963616336666637616434623964303939333765613465222c223735223a2230363639383037316331303532623031386662633035353038313934343835323665396461393531346234666466346261636139393835383661326132383761222c226333223a2262343533653739393939393235343333333238626137333538633065393938666630313430623332386266623665666163663638656362353761393831313333222c226566223a2264663039613364373531353832643535386465616337396466373665633064356439363236353231303666613736373866613930353562376237626561316564222c226630223a2236636230666634643537313362313932383761626439383032333532343133646533616338343632626631653037393164656137393432386461303538653063227d2c2270223a22222c22746e223a312c2276223a6e756c6c7d", + "df09a3d751582d558deac79df76ec0d5d962652106fa7678fa9055b7b7bea1ed": "7b2263223a7b7d2c2270223a2262666266222c22746e223a312c2276223a2261346535393535303163643662363962623366393739396632373136656333656165383061353834336135313266336165353031663735333933326332376635227d", + "ea519cc1e32908111626ced255962d08222c42c00bd49598bef7f717c1cea0ac": "7b22727376223a312c2276616c223a22612070726f70657220707265666978206f6620616c696365227d", + "ed32291ea33f8f6b24e9d0fe8b6011e82ca4ec5e4c3564cdf2cc38f8b41bcc5d": "7b2263223a7b223733223a2238356538636438643762666339316230656563616232343262626231356336306666383934303230323730316263303639663932636162326134373134353964227d2c2270223a223735222c22746e223a312c2276223a2230636538663030663833393462643636386635376432326135343665343432626363386530303564373932646437613164353766333731363932333961646535227d" + } + }, + { + "name": "random-1/sha256", + "addressing": "sha256", + "steps": [ + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "commit", + "root": "f49bddb1249c6dfd201087eee693923693ab7300822a63afb46d24765ef15b2f" + }, + { + "op": "put", + "key": "😀", + "value": "😀x/" + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "commit", + "root": "97af82cb8e859e0e6dc098be627095926fa8dac364950a43627a70527efedfc2" + }, + { + "op": "put", + "key": "users/￿", + "value": 527401.990262979 + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "commit", + "root": "97af82cb8e859e0e6dc098be627095926fa8dac364950a43627a70527efedfc2" + }, + { + "op": "put", + "key": "u/user/ab", + "value": false + }, + { + "op": "commit", + "root": "fdf60350995af2f50c18457ecbdaad82f89ef4972f5ed89eb58582f60faddac9" + }, + { + "op": "commit", + "root": "fdf60350995af2f50c18457ecbdaad82f89ef4972f5ed89eb58582f60faddac9" + }, + { + "op": "put", + "key": "a/ab/ab", + "value": { + "😀b\u00011\\\n": 879078303060946, + "c\téxz": [ + 4193135724629501, + null, + -212800.62724417204 + ], + "中1\\é\"😀\"\t": "￿a\\éyé中0", + "b\u0001\"😀中0": [ + 639.8696662871351, + false, + false + ] + } + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "put", + "key": "a/ab", + "value": false + }, + { + "op": "put", + "key": "user", + "value": "z9
 \t/\u0001\u0001" + }, + { + "op": "put", + "key": "é/a", + "value": "9" + }, + { + "op": "commit", + "root": "256f3780a2f49185dbf58b04518d30f44511dc21a0e8503726e8d9ad3e13f9dd" + }, + { + "op": "put", + "key": "é/users/a", + "value": null + }, + { + "op": "put", + "key": "user", + "value": { + "中1
é\t1": [], + "😀/
\nb ": -5185773715815691, + "": "c", + " z\n😀": "a中" + } + }, + { + "op": "commit", + "root": "d68880432d31c5687aa4a45a29537a9e7100746732a8025b08f6795954c3e325" + }, + { + "op": "put", + "key": "😀/user/x", + "value": [] + }, + { + "op": "put", + "key": "x", + "value": { + "\u0001x\\": "\u0001a/￿\\ az", + "/😀y": 1152921504606847415, + "x\\中\"": [ + "cbc", + true, + "9/￿" + ], + "9\"//x 1￿": { + "x/b\nc\\yy": 2075006567467046 + } + } + }, + { + "op": "put", + "key": "x", + "value": [ + false + ] + }, + { + "op": "put", + "key": "u/😀/ab", + "value": false + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": 571024.2686891332 + }, + { + "op": "delete", + "key": "a/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/￿/ab", + "value": { + "0": 1152921504606847016, + "": "\t\\/\\c" + } + }, + { + "op": "put", + "key": "😀/u/ab", + "value": -288346.0334079566 + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "delete", + "key": "users/abc", + "existed": false + }, + { + "op": "put", + "key": "ab/a/abc", + "value": false + }, + { + "op": "put", + "key": "a", + "value": null + }, + { + "op": "delete", + "key": "ab/￿/ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "delete", + "key": "😀/u/ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": [ + false, + 4975737563517114 + ] + }, + { + "op": "put", + "key": "abc", + "value": "é" + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "commit", + "root": "35c8a0d0b0acf279b49910699e01866ffe34d9707218df523565d1ba01d49e3a" + }, + { + "op": "put", + "key": "abc", + "value": [ + -1213485891030941 + ] + }, + { + "op": "put", + "key": "a", + "value": "\t" + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x/users", + "value": null + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "é/x", + "value": { + "1": {} + } + }, + { + "op": "put", + "key": "😀/a/a", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "c764a1ece6c78195ee8c786e56e19f189a925e7787c42a5d882ef420b192c7f1" + }, + { + "op": "delete", + "key": "é/a", + "existed": true + }, + { + "op": "commit", + "root": "d2727532869754008b0e3d5cbdbbf86d2bda6e97b6c6aed4f94abfd35abb8bfd" + }, + { + "op": "commit", + "root": "d2727532869754008b0e3d5cbdbbf86d2bda6e97b6c6aed4f94abfd35abb8bfd" + }, + { + "op": "put", + "key": "u", + "value": [ + 4792331001286011, + -376395.9363293954 + ] + }, + { + "op": "delete", + "key": "é/users/a", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": [ + { + "9\n": false + }, + null, + { + "\\éz中b": "9", + "9": 1152921504606847910, + "￿c": -0.5180194084166361 + }, + -3144101130451274 + ] + }, + { + "op": "put", + "key": "a/u", + "value": { + "\nx 91\\中a": 0.1581652465616863, + "z y": [ + 1544518820599314 + ], + "\tz中\"": { + "0": "0 xax😀", + "": "
ycé", + "😀 \né\"": "ax\t\t\"" + }, + "中\\/😀": {} + } + }, + { + "op": "delete", + "key": "ab/a/abc", + "existed": true + }, + { + "op": "put", + "key": "x/￿/a", + "value": { + "\n z\t￿é0\"": [], + "😀\n\\/￿😀": true, + "

 ": null, + "
y
\\9z": false + } + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "é/x", + "existed": true + }, + { + "op": "put", + "key": "ab", + "value": { + "éb9éx😀\n": true + } + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "delete", + "key": "a/ab/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/a/￿", + "value": 0.023037844265266116 + }, + { + "op": "delete", + "key": "x/users", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": [ + 5219989364590686, + "19￿1a", + { + "190c
z": "😀y￿9\téz", + "y\t": 4528391509020087 + } + ] + }, + { + "op": "put", + "key": "a", + "value": "x" + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "put", + "key": "users", + "value": { + "/9x￿zx1": { + "\t\\z11 \t": "0\t9/\u0001😀" + }, + "0": true, + "": { + "😀 0\\z
": false, + "": null + }, + "y中b😀\\9": -668528497962439 + } + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "commit", + "root": "86851839ac8cb1de3625a150c1d93526d3a00a14a3e5408fac000c07943972a1" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "ab/a/😀", + "value": { + "9

1": 0.8703319994801906, + "
éb\"中\n中0": [ + -18446744073709551616, + 1152921504606847601, + 8582122809182276 + ] + } + }, + { + "op": "put", + "key": "a", + "value": {} + }, + { + "op": "put", + "key": "￿", + "value": { + "x/by中": null, + "y\\\taé9c": "c b\\b", + "/y9\\": false, + "x\n1é": [ + -2906383224920196 + ] + } + }, + { + "op": "put", + "key": "😀/u/user", + "value": { + "😀é中a z0\"": { + "x\n\"y😀": null, + "": 5385660561127926, + "/\n /\"9/é": "", + "xy///😀c\t": -826100539360698 + }, + "\\c😀by": null, + "\u0001😀91😀/\"
": -18446744073709551616, + "￿/中ézay": "😀yx" + } + }, + { + "op": "put", + "key": "x/users/u", + "value": [ + true, + false + ] + }, + { + "op": "put", + "key": "u", + "value": 8.756506606108011e-11 + }, + { + "op": "put", + "key": "ab/a/a", + "value": { + "\t\"中0\u0001c": "0a中\\é\u0001" + } + }, + { + "op": "put", + "key": "x/x/é", + "value": null + }, + { + "op": "put", + "key": "a", + "value": "" + }, + { + "op": "put", + "key": "abc/￿/x", + "value": [ + [ + "\n￿
😀 \t y", + "😀y中z9
a", + 0.13181702846660892 + ], + null + ] + }, + { + "op": "commit", + "root": "433bdb69ba8ba6c13e24a7d604a71f78536e008007200031f606d7b406193b6d" + }, + { + "op": "commit", + "root": "433bdb69ba8ba6c13e24a7d604a71f78536e008007200031f606d7b406193b6d" + }, + { + "op": "delete", + "key": "x/￿/a", + "existed": true + }, + { + "op": "put", + "key": "😀/abc/é", + "value": {} + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "commit", + "root": "3c3c82b2e33a81f0e44b13f08518f2fb0866659263d0aa3c387a3f5d873e949d" + }, + { + "op": "delete", + "key": "ab/a/￿", + "existed": true + }, + { + "op": "put", + "key": "ab/￿", + "value": "9\n/\u00010\u0001" + }, + { + "op": "commit", + "root": "654f26a01f27360b7d73587b09206afe38d9b59b404eaf606b96209f96619c85" + }, + { + "op": "put", + "key": "user", + "value": 933029.5942664645 + }, + { + "op": "put", + "key": "user/users", + "value": true + }, + { + "op": "commit", + "root": "3e29ede181ab48b84015b9d41a9ca4008154fbd59246046555ea864f089c7821" + }, + { + "op": "put", + "key": "abc/users", + "value": false + }, + { + "op": "delete", + "key": "ab/a/a", + "existed": true + }, + { + "op": "commit", + "root": "2a7d11f0717fe04916de94a4b052a14f4b87b838776417831428e780a0e9f147" + }, + { + "op": "delete", + "key": "a/u", + "existed": true + }, + { + "op": "delete", + "key": "ab/a/😀", + "existed": true + }, + { + "op": "commit", + "root": "b607517cee2f7a69a1ba6da7034e45bce869543ca6d788d07da657391414af70" + }, + { + "op": "put", + "key": "￿/😀/é", + "value": false + }, + { + "op": "put", + "key": "x", + "value": -0.48478091632419784 + }, + { + "op": "commit", + "root": "3fb232a0295f5f654ca483f3b4775d30d474525a0001be5765d83f31081efe2e" + }, + { + "op": "delete", + "key": "u/user/ab", + "existed": true + }, + { + "op": "delete", + "key": "￿/😀/é", + "existed": true + }, + { + "op": "put", + "key": "abc/ab/users", + "value": [ + "1", + [ + -4569361133236593, + 4.669460788724153e-09 + ], + [ + -459675.57609392283 + ] + ] + }, + { + "op": "put", + "key": "abc", + "value": 4664229710873395 + }, + { + "op": "put", + "key": "😀/￿/user", + "value": null + }, + { + "op": "put", + "key": "a", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "user", + "value": "c
中" + }, + { + "op": "delete", + "key": "abc/users", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "é/abc", + "value": null + }, + { + "op": "delete", + "key": "😀/u/user", + "existed": true + }, + { + "op": "put", + "key": "abc/ab", + "value": [ + null, + true, + -4488798683016691 + ] + }, + { + "op": "commit", + "root": "24c389e8f1d74be4b547edad67f0e4f202287d798544ce6802c71f79fb3ec22b" + }, + { + "op": "put", + "key": "u/u", + "value": true + }, + { + "op": "put", + "key": "users/u", + "value": -1813558849521113 + }, + { + "op": "put", + "key": "a/u/abc", + "value": "" + }, + { + "op": "put", + "key": "abc", + "value": 7696377839440766 + }, + { + "op": "commit", + "root": "5995016dd55223792bef2364e3c53d95be7523770da42c7bd04d0277fd376937" + }, + { + "op": "put", + "key": "￿/😀/x", + "value": { + " 1
 中y": null, + "xz10\n9中a": "9é9\u0001y\\x\"", + "
": [ + "é😀a￿ \tyy", + null + ], + "0\u0001": -18446744073709551616 + } + }, + { + "op": "put", + "key": "😀/é", + "value": false + }, + { + "op": "put", + "key": "x/users/￿", + "value": null + }, + { + "op": "commit", + "root": "c40fe5a1e014e3a4f35af9c6c8141f09a6b9fcae2ebaee3ca887be737d9938e5" + }, + { + "op": "delete", + "key": "x/x/é", + "existed": true + }, + { + "op": "commit", + "root": "edc11c4e2e58ca6f5af2882b8a17ed8ab5d774b8922c1f70ca35e93fb7af4a75" + }, + { + "op": "put", + "key": "é", + "value": { + "\n\\￿\tb": true, + "": null + } + }, + { + "op": "delete", + "key": "é/abc", + "existed": true + }, + { + "op": "put", + "key": "￿/ab", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "😀/é", + "existed": true + }, + { + "op": "delete", + "key": "user/users", + "existed": true + }, + { + "op": "commit", + "root": "1286a718dbae402bceba24143d2025ec6962ace780e6bbc5739dff5e82316179" + }, + { + "op": "put", + "key": "abc", + "value": { + "
b￿\n": { + " /\t1
": "yb/xéz中
", + "/x😀a\u00010\\": 1152921504606847829, + "1x1//1": -18446744073709551616 + }, + "\u0001
0\n\t": { + "😀": 0.6794943530144515, + "a": 1152921504606847052, + "\\é yyéx": -640753205298490, + "
1中\\a中": false + } + } + }, + { + "op": "commit", + "root": "bb3475c9b412b933bce07868c3db24b005b0564c0689f8a4b79f60bb50947ba2" + }, + { + "op": "delete", + "key": "abc/￿/x", + "existed": true + }, + { + "op": "put", + "key": "user/user/é", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": 400611.7210392563 + }, + { + "op": "put", + "key": "x", + "value": null + }, + { + "op": "put", + "key": "😀/users/￿", + "value": -0.9582978493681851 + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "commit", + "root": "1ac630e72a7075785fcfa208b3f3955d5e6fc06590c60eec3d64346b0055e841" + }, + { + "op": "delete", + "key": "￿/ab", + "existed": true + }, + { + "op": "delete", + "key": "user/é/é", + "existed": false + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": "￿c" + }, + { + "op": "delete", + "key": "abc/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/x", + "value": {} + }, + { + "op": "put", + "key": "ab", + "value": 5.838011418028465 + }, + { + "op": "put", + "key": "user/￿/😀", + "value": { + "
": true + } + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "x/user/￿", + "value": [] + }, + { + "op": "commit", + "root": "85a8506710498b6467bd3dc081d51b2bf471ced17d2853bfe8df425a691e3a32" + }, + { + "op": "delete", + "key": "u/u", + "existed": true + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "😀", + "value": [ + false, + "\ta " + ] + }, + { + "op": "delete", + "key": "ab/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/u", + "existed": true + }, + { + "op": "delete", + "key": "x/users/￿", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": false + }, + { + "op": "delete", + "key": "user/user/é", + "existed": true + }, + { + "op": "put", + "key": "a/é/￿", + "value": 3895616142526731 + }, + { + "op": "put", + "key": "u/a/x", + "value": { + "￿19b
z中é": 0.9355034421934179, + "\u0001xyz": [], + "b\u00010\\中/1": true + } + }, + { + "op": "delete", + "key": "￿/😀/x", + "existed": true + }, + { + "op": "put", + "key": "￿", + "value": -0.23039267179624923 + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": -6346746654702813 + }, + { + "op": "commit", + "root": "a3b3e80d408a76fbec1bc0ca819ba7d65d4ae355efc6265ab4cc9cc100f9ca76" + }, + { + "op": "commit", + "root": "a3b3e80d408a76fbec1bc0ca819ba7d65d4ae355efc6265ab4cc9cc100f9ca76" + }, + { + "op": "put", + "key": "x", + "value": {} + }, + { + "op": "commit", + "root": "822c6fad39d7e117738572efa3e7d5c2e671880d51066fe348b40d855041ff3f" + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "users/￿", + "value": {} + }, + { + "op": "put", + "key": "user/x", + "value": [ + false, + -18446744073709551616 + ] + }, + { + "op": "commit", + "root": "be9aa988aa46937e0484880119f0dac43d42add57d593e9eb202e1e185be8c97" + }, + { + "op": "delete", + "key": "abc/ab/users", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "x/user/￿", + "existed": true + }, + { + "op": "put", + "key": "u/￿/😀", + "value": 1152921504606847488 + }, + { + "op": "commit", + "root": "7559fb5723a11392d9d261b8dd6500b2ea1903d3283faacf13d7a4f85c58d90e" + }, + { + "op": "commit", + "root": "7559fb5723a11392d9d261b8dd6500b2ea1903d3283faacf13d7a4f85c58d90e" + }, + { + "op": "put", + "key": "x", + "value": -7063685366348132 + }, + { + "op": "put", + "key": "abc/abc", + "value": "bc
19" + }, + { + "op": "delete", + "key": "user/x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": "cy 中
9" + }, + { + "op": "put", + "key": "😀", + "value": null + }, + { + "op": "delete", + "key": "u/a/x", + "existed": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "user/users/😀", + "value": 1152921504606847794 + }, + { + "op": "put", + "key": "user/￿/users", + "value": [ + 7263133179168950, + -534321.9471758215, + "0b\"\t/￿\"" + ] + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "users", + "value": 8.353777129648925e-10 + }, + { + "op": "put", + "key": "😀", + "value": [ + 1152921504606847377, + -0.6249474185050428 + ] + }, + { + "op": "delete", + "key": "x/users/u", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": { + "/c
\n0": [ + "é\u0001￿😀\t￿\u0001z", + 5753815969464206, + "😀\\￿中" + ], + "9 a￿": null, + "\t\"1é\t0\u0001": 1152921504606847688, + "\nb
x\"a": "b \\a// 😀" + } + }, + { + "op": "commit", + "root": "7d0e8c1eaad8ea25881dd3265c51175d05e7c702dcf65f14b89988c9826829f0" + }, + { + "op": "delete", + "key": "😀/￿/user", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": 3436522652239250 + }, + { + "op": "put", + "key": "a/users", + "value": 4795755137747556 + }, + { + "op": "delete", + "key": "😀/abc/é", + "existed": true + }, + { + "op": "put", + "key": "￿/😀", + "value": false + }, + { + "op": "put", + "key": "x", + "value": 45.521129922139956 + }, + { + "op": "put", + "key": "abc/users/ab", + "value": { + "ya": -18446744073709551616, + "\u0001/￿x😀
 中": "a c
\u0001x", + "1￿
9\n\"1b": false + } + }, + { + "op": "put", + "key": "ab", + "value": null + }, + { + "op": "commit", + "root": "ddd3830da592611a35f2e9660ddcbefca96b69383eb23f523f6b79fa3cae4a49" + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "￿", + "value": 1152921504606847316 + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "￿/ab/a", + "value": -1955416100330730 + }, + { + "op": "commit", + "root": "9f20c1f12a971b293b357d3d304772f04c9d0a6372c1c9f219db7a9eea90b532" + }, + { + "op": "put", + "key": "users/abc", + "value": 1152921504606847392 + }, + { + "op": "put", + "key": "u", + "value": -4955888810519122 + }, + { + "op": "put", + "key": "users", + "value": 1152921504606847875 + }, + { + "op": "put", + "key": "users", + "value": "b😀c\t0" + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": false + }, + { + "op": "delete", + "key": "u/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "abc/users/ab", + "existed": true + }, + { + "op": "put", + "key": "x/é/é", + "value": true + }, + { + "op": "put", + "key": "user/x", + "value": true + }, + { + "op": "delete", + "key": "user/users/😀", + "existed": true + }, + { + "op": "put", + "key": "abc/é", + "value": { + "/\\\u0001éa": -18446744073709551616, + " 😀": 874222861798044 + } + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "delete", + "key": "abc/abc", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "7062cc2bc12b1a294f7c53e3f7e3b405b5a2c95fb5a4d27b8a08cae008619e0a" + }, + { + "op": "put", + "key": "abc/😀/u", + "value": -3317481339064278 + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "commit", + "root": "80aefb3d3de442cf84c368a0135f20bbec8ce6d0672d1dd7d92c4cf9d686305e" + }, + { + "op": "delete", + "key": "user/￿/😀", + "existed": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "commit", + "root": "7aded1871f13105d8b0e6ac93c63159f4f23efab4ccf15eb6d56fb105224d819" + }, + { + "op": "delete", + "key": "😀/users/￿", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": null + }, + { + "op": "commit", + "root": "c696f7d2ad7b9352684b6416758f0a1d81a3bca30b8682c85aa535dc6111b2d4" + }, + { + "op": "commit", + "root": "c696f7d2ad7b9352684b6416758f0a1d81a3bca30b8682c85aa535dc6111b2d4" + }, + { + "op": "delete", + "key": "a/users", + "existed": true + }, + { + "op": "put", + "key": "x/é", + "value": -280519.6378761772 + }, + { + "op": "put", + "key": "abc", + "value": [ + -4898538352726972, + true + ] + }, + { + "op": "put", + "key": "x/￿/é", + "value": { + "\u0001zé\"0\nc9": -542121.4288026612, + "\"zb\"c￿1": -825844.6389250358, + "": -0.03927420747133792, + "中\"y\u0001cé/": "😀c\u0001/\n" + } + }, + { + "op": "commit", + "root": "d33f7f69a6993836d8f70662b92f9d42a4c6d00d2c3a142c64bccde85becd468" + }, + { + "op": "put", + "key": "abc/users/user", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "😀/é", + "value": null + }, + { + "op": "put", + "key": "u", + "value": { + "9\"
": { + "￿9z/": true, + "\"é😀0\\\t": false, + "b
1": null, + "b": "\u0001😀\u0001/éz😀\u0001" + }, + "a\\中中\tz": -18446744073709551616, + "\t1
中 ": true + } + }, + { + "op": "commit", + "root": "464186378824ab72aeb5aeec65dfc188d05609cee51db80404f29703ef37a9f0" + }, + { + "op": "put", + "key": "users", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "67b8bdd634743e1af936ad1b08f8501ffbb3f64bc6784fa62cb541ff0de5da89" + }, + { + "op": "put", + "key": "x", + "value": 0.04996968708111881 + }, + { + "op": "put", + "key": "u/abc", + "value": "z/" + }, + { + "op": "put", + "key": "x/￿/ab", + "value": -0.14518588083877848 + }, + { + "op": "put", + "key": "x/a", + "value": "a/" + }, + { + "op": "put", + "key": "￿/user/x", + "value": { + "\t\"0bcxx中": { + "\\z\u0001\té😀b": "😀\t\u0001" + }, + " \"z￿9z": null, + "bc中1\t/\t/": true + } + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": [ + { + "/y": -6535351210100194, + "10\t": 1152921504606847874, + "x": -0.6965929718411152, + "a\\\u0001\t ": -18446744073709551616 + }, + -18446744073709551616, + -191842873337320 + ] + }, + { + "op": "put", + "key": "u", + "value": [] + }, + { + "op": "put", + "key": "￿/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/user/a", + "value": [ + -0.35811442277877137 + ] + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/abc", + "value": { + "a\\": 1152921504606847594, + "c￿/": { + "x\na\"": null + }, + "yx中z/y": { + "中
é90": true, + "zyx\t😀éy\n": false, + "￿ya\"z": true + } + } + }, + { + "op": "put", + "key": "u/user/￿", + "value": { + "0\"": 7091232726655776, + "c\\\u0001aé": { + "0a ": null + }, + "é0cx": false, + "x\t😀é
": { + "\n\"": -18446744073709551616, + "\n\"中00c": true + } + } + }, + { + "op": "put", + "key": "x/😀", + "value": true + }, + { + "op": "delete", + "key": "a/abc", + "existed": true + }, + { + "op": "put", + "key": "é/user", + "value": 9151900423.560808 + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "ab/x", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": { + "": 1152921504606847109 + } + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "commit", + "root": "b130b6af519d5e2217072a1f6add8b3f5207d9cae57a758f0ea932e91636d594" + }, + { + "op": "put", + "key": "￿", + "value": [] + }, + { + "op": "put", + "key": "é/x/￿", + "value": { + "\\\u0001/\txc0😀": 1152921504606847082 + } + }, + { + "op": "put", + "key": "u", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": { + "": { + "\\
b￿": null, + "￿\u0001\t1": 8931341152562429, + "\u0001\t中b9": "\t\t", + "😀😀z/": "\n中￿\\\u0001
" + }, + " ac": 8515762626616835, + "\"": "中 yx", + "y\tb\t\u0001😀/中": 1152921504606847104 + } + }, + { + "op": "put", + "key": "u/😀/ab", + "value": null + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "abc/😀/u", + "existed": true + }, + { + "op": "put", + "key": "x/😀/u", + "value": { + "😀b0zb": false, + "中": [ + true, + true + ], + "cé\"": { + " 😀": true, + "ba\n￿": -457337379954808 + }, + "\ncz0b
": { + "\"éyz1": false, + "": 6623239290182256, + "/019yé\\": null + } + } + }, + { + "op": "put", + "key": "users/😀/ab", + "value": "0y
\\" + }, + { + "op": "delete", + "key": "x/😀/u", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": {} + }, + { + "op": "commit", + "root": "b0bffe20bba991325eca20de5b47d8114a0b108db726b177111ad7a53255602a" + }, + { + "op": "delete", + "key": "😀/a/a", + "existed": true + }, + { + "op": "put", + "key": "x/u", + "value": -2549254231391525 + }, + { + "op": "delete", + "key": "é/é/￿", + "existed": false + }, + { + "op": "commit", + "root": "e2785c18856dc1e4e87e0daf1fa40dde38399a23c189ef23f891cf610828db01" + }, + { + "op": "delete", + "key": "x/😀", + "existed": true + }, + { + "op": "commit", + "root": "36e2cb997f04a51f2e8d4cb15af27ba40119489481efb2f4a0f9c3020827b162" + }, + { + "op": "put", + "key": "ab", + "value": { + "
x0￿9\u0001": { + "x/y中中9a中": 1152921504606847726, + "\t9x y": null + }, + "\\a\u0001😀y中": { + "éa\\": null, + "￿c1b\tc": -8294236355241357, + "bccb😀": -3460883124835219 + }, + "中": { + "/z
\"é": true, + "1
0": 8872401519835205, + "中/ 😀": null, + "91y1zc9": -0.6700163064224591 + }, + "0zb\t0\\": false + } + }, + { + "op": "put", + "key": "ab/￿/😀", + "value": 1152921504606847061 + }, + { + "op": "put", + "key": "😀/￿/😀", + "value": null + }, + { + "op": "delete", + "key": "é/user", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": "é￿ " + }, + { + "op": "commit", + "root": "2d063f02bcce304a1b74d05eb9b29d54aec6e889865d4c13e43d4be6eca394db" + }, + { + "op": "put", + "key": "users/users", + "value": null + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": true + }, + { + "op": "put", + "key": "x/users/😀", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "put", + "key": "user", + "value": 1152921504606847911 + }, + { + "op": "put", + "key": "users/￿", + "value": "9中\\\\é" + }, + { + "op": "put", + "key": "user", + "value": { + "y￿0éaé": null, + "y1/\\b": { + "écby中\n中": 29584211.613887373, + " éa😀90": "中z1c0\u0001zb", + " ayxb😀": -0.649562911066861, + "\\": true + }, + "é\u0001\u0001é\"": { + "\t9\\": 8884777165940577, + "\\￿x": false, + "ca\n😀\u0001": null, + "x

1\t\"é": null + }, + "b9😀é￿": 1152921504606847108 + } + }, + { + "op": "put", + "key": "a/😀", + "value": {} + }, + { + "op": "commit", + "root": "2b79cf1c6588589731d30522b2b76bf112caddd905d7bb5b83d244f5d4a99bc9" + }, + { + "op": "put", + "key": "x", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "put", + "key": "😀/u", + "value": { + "b": null, + "": "", + " z中\u0001": -18446744073709551616, + "11\"ba": { + "\\yz": true, + "": true, + "中￿ézb\n1": 344532283821597, + "0b￿\\\n\\é": -0.3260019933467111 + } + } + }, + { + "op": "delete", + "key": "u/abc", + "existed": true + }, + { + "op": "commit", + "root": "90d007c4a4cdd9f04ab336caeb117c93a17c2afbaa587397c47968a67fecf5f2" + }, + { + "op": "put", + "key": "￿/x/user", + "value": "\u0001 \"\t\\中" + }, + { + "op": "put", + "key": "￿/￿/a", + "value": "" + }, + { + "op": "commit", + "root": "dc4b6ef34887aac83efee7f922bb10c775a3f41c9278686ebfbbda01d9ae1e42" + }, + { + "op": "put", + "key": "user/ab", + "value": "az\u0001x1yx" + }, + { + "op": "put", + "key": "u", + "value": { + "ab9bé\u0001
": -0.12433582379621178, + " c1\t中\"": { + "😀9zyb/": -2225304300974304, + "
": "😀zy
x", + "0\u00011\"￿é
z": true + }, + "
\\": { + "b中ca\"": true, + "\té😀9\n￿": -324841581102270, + "c😀￿by": "zcacaz9" + }, + "\t\\中": [ + -18446744073709551616, + "x\\\t1c/" + ] + } + }, + { + "op": "put", + "key": "é/users/ab", + "value": 1152921504606847765 + }, + { + "op": "delete", + "key": "￿/x/user", + "existed": true + }, + { + "op": "put", + "key": "é/users/x", + "value": {} + }, + { + "op": "put", + "key": "users/users/￿", + "value": { + "\\b": 7927229420658014, + "é￿\n
": 1265880572805915 + } + }, + { + "op": "put", + "key": "ab/users/é", + "value": [ + { + "z\t\\😀中": 1152921504606847104 + }, + { + "\u0001b
 z
": -4068212830225738, + "9": null + }, + false, + true + ] + }, + { + "op": "put", + "key": "users", + "value": 1.8686463799365743e-10 + }, + { + "op": "delete", + "key": "x/a", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": 0.1482451034984853 + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "delete", + "key": "abc/é", + "existed": true + }, + { + "op": "put", + "key": "users/users/x", + "value": "\t" + }, + { + "op": "put", + "key": "ab", + "value": [ + "\\x
1 
y", + [ + 1152921504606847937, + 3772187621166608, + -811193.049054771, + 4674140977728952 + ] + ] + }, + { + "op": "delete", + "key": "￿/￿/a", + "existed": true + }, + { + "op": "put", + "key": "users/a/😀", + "value": 1152921504606847632 + }, + { + "op": "put", + "key": "😀/u/ab", + "value": null + }, + { + "op": "put", + "key": "a/a", + "value": -0.515913436172214 + }, + { + "op": "delete", + "key": "x/é", + "existed": true + }, + { + "op": "put", + "key": "u/a", + "value": true + }, + { + "op": "put", + "key": "a/user/a", + "value": "/\"9😀y" + }, + { + "op": "put", + "key": "x/￿", + "value": null + }, + { + "op": "put", + "key": "a", + "value": "c\n\"" + }, + { + "op": "delete", + "key": "u/a", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "é/x/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/users", + "existed": true + }, + { + "op": "delete", + "key": "a/x", + "existed": false + }, + { + "op": "put", + "key": "users/u", + "value": [] + }, + { + "op": "put", + "key": "😀/ab/é", + "value": { + "": true, + "\u0001\\\u0001": true, + "\u00010": "b y9
é ", + "y\n/é/09": null + } + }, + { + "op": "put", + "key": "ab/😀/ab", + "value": " 9" + }, + { + "op": "delete", + "key": "ab/😀/ab", + "existed": true + }, + { + "op": "put", + "key": "😀/user", + "value": {} + }, + { + "op": "put", + "key": "users/￿", + "value": "" + }, + { + "op": "put", + "key": "x", + "value": 1152921504606847108 + }, + { + "op": "put", + "key": "é/a/￿", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": 704189.2676369569 + }, + { + "op": "put", + "key": "u/é/ab", + "value": {} + }, + { + "op": "commit", + "root": "341a3436bea484f671cf413741497a948aeef15d50681c230bc047ed49b805b0" + }, + { + "op": "put", + "key": "x/a", + "value": [] + }, + { + "op": "put", + "key": "user/a/￿", + "value": {} + }, + { + "op": "delete", + "key": "users/users/x", + "existed": true + }, + { + "op": "put", + "key": "users/ab", + "value": true + }, + { + "op": "put", + "key": "ab/u", + "value": "" + }, + { + "op": "put", + "key": "a/user/users", + "value": { + "": [ + null, + -18446744073709551616 + ], + "9": 3545523840262856, + "cz😀": { + "": -18446744073709551616, + "b\\": 1152921504606847400, + "1c": "中中/90é￿" + }, + "\\1 ￿": "
9" + } + }, + { + "op": "put", + "key": "a/a/￿", + "value": null + }, + { + "op": "put", + "key": "abc/a/user", + "value": null + }, + { + "op": "delete", + "key": "u/user/￿", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": -52867956797762 + }, + { + "op": "commit", + "root": "8d5f4cd8ed87980e2abfcfad449cfff7f2e36ae63b1fd3b198998bf8ae43aec6" + }, + { + "op": "put", + "key": "abc/x", + "value": -694680.7535715397 + }, + { + "op": "put", + "key": "😀", + "value": "" + }, + { + "op": "put", + "key": "x/😀/é", + "value": { + "￿\u00010\n\\": "c", + "/\"": { + "\\": null, + "￿0x1\u0001\\": false, + "￿x\"": -1090520089078268 + }, + "\\
": { + "bé": false, + "x9": "", + "": 5979433220707924, + "b y": true + } + } + }, + { + "op": "put", + "key": "u", + "value": 47507468532721.555 + }, + { + "op": "delete", + "key": "a/user/a", + "existed": true + }, + { + "op": "delete", + "key": "a/user/users", + "existed": true + }, + { + "op": "delete", + "key": "abc/users/user", + "existed": true + }, + { + "op": "delete", + "key": "ab/￿/😀", + "existed": true + }, + { + "op": "put", + "key": "abc/u/abc", + "value": [ + 1205982392274335, + { + "😀中1y中😀x\n": -18446744073709551616, + "\t中\\": 1210956020351507, + "bb": 2144588251547270 + } + ] + }, + { + "op": "put", + "key": "x/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x/a", + "value": -5037139323945801 + }, + { + "op": "put", + "key": "a", + "value": { + " \u0001b": { + "": -3613743620030602 + }, + "\na\t": 1152921504606847405, + "z": [], + "zaé\u0001\u0001\"": [ + null, + -18446744073709551616, + 1.0730825888967211e-10 + ] + } + }, + { + "op": "commit", + "root": "38b8c9da21faa9a5ca1e2f2174188eafe7b71daf820ba2bf069943a48a392118" + }, + { + "op": "delete", + "key": "😀/user/x", + "existed": true + }, + { + "op": "commit", + "root": "28c3cf1a70516f3366dfa08c36851aef9ecf03c5c9bec5545e831013fb99b761" + }, + { + "op": "put", + "key": "user/user", + "value": "\\céxc\\\u0001\"" + }, + { + "op": "put", + "key": "😀", + "value": 9791490748.585184 + }, + { + "op": "delete", + "key": "x/￿/a", + "existed": false + }, + { + "op": "delete", + "key": "users/users/x", + "existed": false + }, + { + "op": "put", + "key": "user/😀/￿", + "value": true + }, + { + "op": "put", + "key": "user/abc/abc", + "value": { + "\tc\\\"a ": 168879.26095807785 + } + }, + { + "op": "put", + "key": "ab/user", + "value": "//0" + }, + { + "op": "commit", + "root": "08dafe1afdc021b350dfbdd83eeac878de19b5a82fa45f51a20e054387fdacde" + }, + { + "op": "put", + "key": "user", + "value": { + "": "9c
1ay\n\"", + "\u0001b\\x": "11é😀\n😀" + } + }, + { + "op": "put", + "key": "user", + "value": { + "c￿1中
": [ + true + ] + } + }, + { + "op": "delete", + "key": "user/abc/abc", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": -8405007150166980 + }, + { + "op": "put", + "key": "users/user", + "value": null + }, + { + "op": "put", + "key": "é/é/ab", + "value": -1331536963664203 + }, + { + "op": "delete", + "key": "user/x", + "existed": true + }, + { + "op": "put", + "key": "users/ab", + "value": 390423.53405178036 + }, + { + "op": "put", + "key": "a/user/é", + "value": { + "z0zc": 1152921504606847331 + } + }, + { + "op": "put", + "key": "a/a/abc", + "value": [ + {}, + { + "zz￿x\"": false, + "￿c\t\n0cc": -18446744073709551616, + "\\\"😀/\n": "0", + "\"é\u0001\"x\t": -4949069302991004 + }, + "y09é￿b", + 1442236461640640 + ] + }, + { + "op": "put", + "key": "ab", + "value": false + }, + { + "op": "delete", + "key": "x/a", + "existed": true + }, + { + "op": "commit", + "root": "f80026d2ae4eec9ae1047f2ce815aca89f40d3e9b9f931f56560ac243f94c84d" + } + ], + "items": [ + [ + "a", + { + "\na\t": 1152921504606847405, + " \u0001b": { + "": -3613743620030602 + }, + "z": [], + "zaé\u0001\u0001\"": [ + null, + -18446744073709551616, + 1.0730825888967211e-10 + ] + } + ], + [ + "a/a", + -0.515913436172214 + ], + [ + "a/a/abc", + [ + {}, + { + "\"é\u0001\"x\t": -4949069302991004, + "\\\"😀/\n": "0", + "zz￿x\"": false, + "￿c\t\n0cc": -18446744073709551616 + }, + "y09é￿b", + 1442236461640640 + ] + ], + [ + "a/a/￿", + null + ], + [ + "a/u/abc", + "" + ], + [ + "a/user/é", + { + "z0zc": 1152921504606847331 + } + ], + [ + "a/é/￿", + 3895616142526731 + ], + [ + "a/😀", + {} + ], + [ + "ab", + false + ], + [ + "ab/u", + "" + ], + [ + "ab/user", + "//0" + ], + [ + "ab/users/é", + [ + { + "z\t\\😀中": 1152921504606847104 + }, + { + "\u0001b
 z
": -4068212830225738, + "9": null + }, + false, + true + ] + ], + [ + "abc", + -18446744073709551616 + ], + [ + "abc/a/user", + null + ], + [ + "abc/u/abc", + [ + 1205982392274335, + { + "\t中\\": 1210956020351507, + "bb": 2144588251547270, + "😀中1y中😀x\n": -18446744073709551616 + } + ] + ], + [ + "abc/x", + -694680.7535715397 + ], + [ + "u", + -8405007150166980 + ], + [ + "u/é/ab", + {} + ], + [ + "u/￿/😀", + 1152921504606847488 + ], + [ + "u/😀/ab", + null + ], + [ + "user", + { + "c￿1中
": [ + true + ] + } + ], + [ + "user/a/￿", + {} + ], + [ + "user/ab", + "az\u0001x1yx" + ], + [ + "user/user", + "\\céxc\\\u0001\"" + ], + [ + "user/￿/users", + [ + 7263133179168950, + -534321.9471758215, + "0b\"\t/￿\"" + ] + ], + [ + "user/😀/￿", + true + ], + [ + "users", + -52867956797762 + ], + [ + "users/a/😀", + 1152921504606847632 + ], + [ + "users/ab", + 390423.53405178036 + ], + [ + "users/abc", + 1152921504606847392 + ], + [ + "users/u", + [] + ], + [ + "users/user", + null + ], + [ + "users/users/￿", + { + "\\b": 7927229420658014, + "é￿\n
": 1265880572805915 + } + ], + [ + "users/￿", + "" + ], + [ + "users/😀/ab", + "0y
\\" + ], + [ + "x", + 1152921504606847108 + ], + [ + "x/abc", + -18446744073709551616 + ], + [ + "x/u", + -2549254231391525 + ], + [ + "x/users/😀", + null + ], + [ + "x/é/é", + true + ], + [ + "x/￿", + null + ], + [ + "x/￿/ab", + -0.14518588083877848 + ], + [ + "x/￿/é", + { + "": -0.03927420747133792, + "\u0001zé\"0\nc9": -542121.4288026612, + "\"zb\"c￿1": -825844.6389250358, + "中\"y\u0001cé/": "😀c\u0001/\n" + } + ], + [ + "x/😀/é", + { + "/\"": { + "\\": null, + "￿0x1\u0001\\": false, + "￿x\"": -1090520089078268 + }, + "\\
": { + "": 5979433220707924, + "b y": true, + "bé": false, + "x9": "" + }, + "￿\u00010\n\\": "c" + } + ], + [ + "é", + -18446744073709551616 + ], + [ + "é/a/￿", + true + ], + [ + "é/users/ab", + 1152921504606847765 + ], + [ + "é/users/x", + {} + ], + [ + "é/é/ab", + -1331536963664203 + ], + [ + "￿", + null + ], + [ + "￿/ab/a", + -1955416100330730 + ], + [ + "￿/abc", + -18446744073709551616 + ], + [ + "￿/user/x", + { + "\t\"0bcxx中": { + "\\z\u0001\té😀b": "😀\t\u0001" + }, + " \"z￿9z": null, + "bc中1\t/\t/": true + } + ], + [ + "￿/😀", + false + ], + [ + "😀", + 9791490748.585184 + ], + [ + "😀/ab/é", + { + "": true, + "\u00010": "b y9
é ", + "\u0001\\\u0001": true, + "y\n/é/09": null + } + ], + [ + "😀/u", + { + "": "", + " z中\u0001": -18446744073709551616, + "11\"ba": { + "": true, + "0b￿\\\n\\é": -0.3260019933467111, + "\\yz": true, + "中￿ézb\n1": 344532283821597 + }, + "b": null + } + ], + [ + "😀/u/ab", + null + ], + [ + "😀/user", + {} + ], + [ + "😀/é", + null + ], + [ + "😀/￿/😀", + null + ] + ] + }, + { + "name": "random-1/swarm", + "addressing": "swarm", + "steps": [ + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "commit", + "root": "6f94e53b65e3e3e9ef3df69e192f66c38aa05378c5a95fe75da3c768d4d44e71" + }, + { + "op": "put", + "key": "😀", + "value": "😀x/" + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "commit", + "root": "99e156f059cbe809c23b477f0fe263d7a67727ea700c1311093bfd550d310665" + }, + { + "op": "put", + "key": "users/￿", + "value": 527401.990262979 + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "commit", + "root": "99e156f059cbe809c23b477f0fe263d7a67727ea700c1311093bfd550d310665" + }, + { + "op": "put", + "key": "u/user/ab", + "value": false + }, + { + "op": "commit", + "root": "dc7b46c23c17feddadd9a95017db90f4a3c95feb04635b9798eb1525c2745b7d" + }, + { + "op": "commit", + "root": "dc7b46c23c17feddadd9a95017db90f4a3c95feb04635b9798eb1525c2745b7d" + }, + { + "op": "put", + "key": "a/ab/ab", + "value": { + "😀b\u00011\\\n": 879078303060946, + "c\téxz": [ + 4193135724629501, + null, + -212800.62724417204 + ], + "中1\\é\"😀\"\t": "￿a\\éyé中0", + "b\u0001\"😀中0": [ + 639.8696662871351, + false, + false + ] + } + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "put", + "key": "a/ab", + "value": false + }, + { + "op": "put", + "key": "user", + "value": "z9
 \t/\u0001\u0001" + }, + { + "op": "put", + "key": "é/a", + "value": "9" + }, + { + "op": "commit", + "root": "a7739da65144a97d4eaf5d42decfa6c5426350fcab6c1fd6bc6c8a3432701c95" + }, + { + "op": "put", + "key": "é/users/a", + "value": null + }, + { + "op": "put", + "key": "user", + "value": { + "中1
é\t1": [], + "😀/
\nb ": -5185773715815691, + "": "c", + " z\n😀": "a中" + } + }, + { + "op": "commit", + "root": "68e9d99c02e2a547eb29d738255a62d709e387100ebc73bce8c26f4065fac57b" + }, + { + "op": "put", + "key": "😀/user/x", + "value": [] + }, + { + "op": "put", + "key": "x", + "value": { + "\u0001x\\": "\u0001a/￿\\ az", + "/😀y": 1152921504606847415, + "x\\中\"": [ + "cbc", + true, + "9/￿" + ], + "9\"//x 1￿": { + "x/b\nc\\yy": 2075006567467046 + } + } + }, + { + "op": "put", + "key": "x", + "value": [ + false + ] + }, + { + "op": "put", + "key": "u/😀/ab", + "value": false + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": 571024.2686891332 + }, + { + "op": "delete", + "key": "a/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/￿/ab", + "value": { + "0": 1152921504606847016, + "": "\t\\/\\c" + } + }, + { + "op": "put", + "key": "😀/u/ab", + "value": -288346.0334079566 + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "delete", + "key": "users/abc", + "existed": false + }, + { + "op": "put", + "key": "ab/a/abc", + "value": false + }, + { + "op": "put", + "key": "a", + "value": null + }, + { + "op": "delete", + "key": "ab/￿/ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "delete", + "key": "😀/u/ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": [ + false, + 4975737563517114 + ] + }, + { + "op": "put", + "key": "abc", + "value": "é" + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "commit", + "root": "7c769d883dbf093b6f60d55dbbaae2c20dd446ea8b1370da221e3b484d905760" + }, + { + "op": "put", + "key": "abc", + "value": [ + -1213485891030941 + ] + }, + { + "op": "put", + "key": "a", + "value": "\t" + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x/users", + "value": null + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "é/x", + "value": { + "1": {} + } + }, + { + "op": "put", + "key": "😀/a/a", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "71e2102081945da53dad7940d53416ae183780e77822a4fd529978605fb368be" + }, + { + "op": "delete", + "key": "é/a", + "existed": true + }, + { + "op": "commit", + "root": "6836664a2d659d336f6d2cb8bca2a4364c1010590c844591726b32f0775c9300" + }, + { + "op": "commit", + "root": "6836664a2d659d336f6d2cb8bca2a4364c1010590c844591726b32f0775c9300" + }, + { + "op": "put", + "key": "u", + "value": [ + 4792331001286011, + -376395.9363293954 + ] + }, + { + "op": "delete", + "key": "é/users/a", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": [ + { + "9\n": false + }, + null, + { + "\\éz中b": "9", + "9": 1152921504606847910, + "￿c": -0.5180194084166361 + }, + -3144101130451274 + ] + }, + { + "op": "put", + "key": "a/u", + "value": { + "\nx 91\\中a": 0.1581652465616863, + "z y": [ + 1544518820599314 + ], + "\tz中\"": { + "0": "0 xax😀", + "": "
ycé", + "😀 \né\"": "ax\t\t\"" + }, + "中\\/😀": {} + } + }, + { + "op": "delete", + "key": "ab/a/abc", + "existed": true + }, + { + "op": "put", + "key": "x/￿/a", + "value": { + "\n z\t￿é0\"": [], + "😀\n\\/￿😀": true, + "

 ": null, + "
y
\\9z": false + } + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "é/x", + "existed": true + }, + { + "op": "put", + "key": "ab", + "value": { + "éb9éx😀\n": true + } + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "delete", + "key": "a/ab/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/a/￿", + "value": 0.023037844265266116 + }, + { + "op": "delete", + "key": "x/users", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": [ + 5219989364590686, + "19￿1a", + { + "190c
z": "😀y￿9\téz", + "y\t": 4528391509020087 + } + ] + }, + { + "op": "put", + "key": "a", + "value": "x" + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "put", + "key": "users", + "value": { + "/9x￿zx1": { + "\t\\z11 \t": "0\t9/\u0001😀" + }, + "0": true, + "": { + "😀 0\\z
": false, + "": null + }, + "y中b😀\\9": -668528497962439 + } + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "commit", + "root": "8720bcf3deee27aab02f74cbdbdd4275ab9d09f29e2680d2ca5bd81dd6a8caf8" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "ab/a/😀", + "value": { + "9

1": 0.8703319994801906, + "
éb\"中\n中0": [ + -18446744073709551616, + 1152921504606847601, + 8582122809182276 + ] + } + }, + { + "op": "put", + "key": "a", + "value": {} + }, + { + "op": "put", + "key": "￿", + "value": { + "x/by中": null, + "y\\\taé9c": "c b\\b", + "/y9\\": false, + "x\n1é": [ + -2906383224920196 + ] + } + }, + { + "op": "put", + "key": "😀/u/user", + "value": { + "😀é中a z0\"": { + "x\n\"y😀": null, + "": 5385660561127926, + "/\n /\"9/é": "", + "xy///😀c\t": -826100539360698 + }, + "\\c😀by": null, + "\u0001😀91😀/\"
": -18446744073709551616, + "￿/中ézay": "😀yx" + } + }, + { + "op": "put", + "key": "x/users/u", + "value": [ + true, + false + ] + }, + { + "op": "put", + "key": "u", + "value": 8.756506606108011e-11 + }, + { + "op": "put", + "key": "ab/a/a", + "value": { + "\t\"中0\u0001c": "0a中\\é\u0001" + } + }, + { + "op": "put", + "key": "x/x/é", + "value": null + }, + { + "op": "put", + "key": "a", + "value": "" + }, + { + "op": "put", + "key": "abc/￿/x", + "value": [ + [ + "\n￿
😀 \t y", + "😀y中z9
a", + 0.13181702846660892 + ], + null + ] + }, + { + "op": "commit", + "root": "3043750bb9bd4b9117ce36974f97e193174d5c6307e0e8e8f2d731c7f30b2bf3" + }, + { + "op": "commit", + "root": "3043750bb9bd4b9117ce36974f97e193174d5c6307e0e8e8f2d731c7f30b2bf3" + }, + { + "op": "delete", + "key": "x/￿/a", + "existed": true + }, + { + "op": "put", + "key": "😀/abc/é", + "value": {} + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "commit", + "root": "9e291fe7e5bf6f73a0dca49a0804c349c0b21b5861fa0365d403c4c61b60f7d9" + }, + { + "op": "delete", + "key": "ab/a/￿", + "existed": true + }, + { + "op": "put", + "key": "ab/￿", + "value": "9\n/\u00010\u0001" + }, + { + "op": "commit", + "root": "03c9ace26ac8b4a62015ded599bfd8c5203b1ad3a2d72c82cc77df95ace92316" + }, + { + "op": "put", + "key": "user", + "value": 933029.5942664645 + }, + { + "op": "put", + "key": "user/users", + "value": true + }, + { + "op": "commit", + "root": "871c77a93e53a44fef69ac693dab4deb1c62e6ab74faf954318bc582217c3415" + }, + { + "op": "put", + "key": "abc/users", + "value": false + }, + { + "op": "delete", + "key": "ab/a/a", + "existed": true + }, + { + "op": "commit", + "root": "9a43a6b02e23edab1ef56db124bf796da20080ea232f9cf873c2cffe4b648644" + }, + { + "op": "delete", + "key": "a/u", + "existed": true + }, + { + "op": "delete", + "key": "ab/a/😀", + "existed": true + }, + { + "op": "commit", + "root": "2f9617115bfc1c752d47dadbd27406a2b3f5f17910e96aa44c1c543d964db9cb" + }, + { + "op": "put", + "key": "￿/😀/é", + "value": false + }, + { + "op": "put", + "key": "x", + "value": -0.48478091632419784 + }, + { + "op": "commit", + "root": "b1f4d90acc53bcdf8d280e7c75283ce6d09b7c1e5fef7fff50940e984e7f2775" + }, + { + "op": "delete", + "key": "u/user/ab", + "existed": true + }, + { + "op": "delete", + "key": "￿/😀/é", + "existed": true + }, + { + "op": "put", + "key": "abc/ab/users", + "value": [ + "1", + [ + -4569361133236593, + 4.669460788724153e-09 + ], + [ + -459675.57609392283 + ] + ] + }, + { + "op": "put", + "key": "abc", + "value": 4664229710873395 + }, + { + "op": "put", + "key": "😀/￿/user", + "value": null + }, + { + "op": "put", + "key": "a", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "user", + "value": "c
中" + }, + { + "op": "delete", + "key": "abc/users", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "é/abc", + "value": null + }, + { + "op": "delete", + "key": "😀/u/user", + "existed": true + }, + { + "op": "put", + "key": "abc/ab", + "value": [ + null, + true, + -4488798683016691 + ] + }, + { + "op": "commit", + "root": "1076255edf73b1701cd42797826dd914fb5aa837246b904fee5a870025b2d0f5" + }, + { + "op": "put", + "key": "u/u", + "value": true + }, + { + "op": "put", + "key": "users/u", + "value": -1813558849521113 + }, + { + "op": "put", + "key": "a/u/abc", + "value": "" + }, + { + "op": "put", + "key": "abc", + "value": 7696377839440766 + }, + { + "op": "commit", + "root": "c1a4855512935f759d0287f54944eedc1337ad7461b2fdac4d550715b3576a40" + }, + { + "op": "put", + "key": "￿/😀/x", + "value": { + " 1
 中y": null, + "xz10\n9中a": "9é9\u0001y\\x\"", + "
": [ + "é😀a￿ \tyy", + null + ], + "0\u0001": -18446744073709551616 + } + }, + { + "op": "put", + "key": "😀/é", + "value": false + }, + { + "op": "put", + "key": "x/users/￿", + "value": null + }, + { + "op": "commit", + "root": "17c1f0469c29cb3c0a6f52bf43bd6f19038c88a0e7da1ad0221815996d0e942d" + }, + { + "op": "delete", + "key": "x/x/é", + "existed": true + }, + { + "op": "commit", + "root": "c6aa001b7e3c6ea5940bb0eada6954b543e6b6cf78893fb21de32b474158302c" + }, + { + "op": "put", + "key": "é", + "value": { + "\n\\￿\tb": true, + "": null + } + }, + { + "op": "delete", + "key": "é/abc", + "existed": true + }, + { + "op": "put", + "key": "￿/ab", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "😀/é", + "existed": true + }, + { + "op": "delete", + "key": "user/users", + "existed": true + }, + { + "op": "commit", + "root": "e67a57f4f50b408ae3644e8457ff3bf8ed530474d65400037789cc28916669bd" + }, + { + "op": "put", + "key": "abc", + "value": { + "
b￿\n": { + " /\t1
": "yb/xéz中
", + "/x😀a\u00010\\": 1152921504606847829, + "1x1//1": -18446744073709551616 + }, + "\u0001
0\n\t": { + "😀": 0.6794943530144515, + "a": 1152921504606847052, + "\\é yyéx": -640753205298490, + "
1中\\a中": false + } + } + }, + { + "op": "commit", + "root": "e556807daddbc04909eed5e8b8a1fe4c7bf7710b3e0a9ba05976761ccaa67977" + }, + { + "op": "delete", + "key": "abc/￿/x", + "existed": true + }, + { + "op": "put", + "key": "user/user/é", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": 400611.7210392563 + }, + { + "op": "put", + "key": "x", + "value": null + }, + { + "op": "put", + "key": "😀/users/￿", + "value": -0.9582978493681851 + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "commit", + "root": "f2c5a9ae328cfd163dc6f4f26f883b8d86778f3a422162d5290eaaab19436146" + }, + { + "op": "delete", + "key": "￿/ab", + "existed": true + }, + { + "op": "delete", + "key": "user/é/é", + "existed": false + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": "￿c" + }, + { + "op": "delete", + "key": "abc/ab", + "existed": true + }, + { + "op": "put", + "key": "ab/x", + "value": {} + }, + { + "op": "put", + "key": "ab", + "value": 5.838011418028465 + }, + { + "op": "put", + "key": "user/￿/😀", + "value": { + "
": true + } + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "x/user/￿", + "value": [] + }, + { + "op": "commit", + "root": "d43827cdca63983d2b19827d20790519e25b7d5d8417ffc46b5aefd08e2ab173" + }, + { + "op": "delete", + "key": "u/u", + "existed": true + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "😀", + "value": [ + false, + "\ta " + ] + }, + { + "op": "delete", + "key": "ab/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/u", + "existed": true + }, + { + "op": "delete", + "key": "x/users/￿", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": false + }, + { + "op": "delete", + "key": "user/user/é", + "existed": true + }, + { + "op": "put", + "key": "a/é/￿", + "value": 3895616142526731 + }, + { + "op": "put", + "key": "u/a/x", + "value": { + "￿19b
z中é": 0.9355034421934179, + "\u0001xyz": [], + "b\u00010\\中/1": true + } + }, + { + "op": "delete", + "key": "￿/😀/x", + "existed": true + }, + { + "op": "put", + "key": "￿", + "value": -0.23039267179624923 + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": -6346746654702813 + }, + { + "op": "commit", + "root": "7b8b3481dac255458e5d9fb3a03d9d59004983befbcf73e6752a5dd39f35b319" + }, + { + "op": "commit", + "root": "7b8b3481dac255458e5d9fb3a03d9d59004983befbcf73e6752a5dd39f35b319" + }, + { + "op": "put", + "key": "x", + "value": {} + }, + { + "op": "commit", + "root": "b7ab0fefee987df5413586fc99006055c3d4b0d4617fb0aac1eef16d08922d7a" + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "users/￿", + "value": {} + }, + { + "op": "put", + "key": "user/x", + "value": [ + false, + -18446744073709551616 + ] + }, + { + "op": "commit", + "root": "8da5ceab70e4e90742f398d4a538499f1b4aa8078a98eeea42a10e6f7707092c" + }, + { + "op": "delete", + "key": "abc/ab/users", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "x/user/￿", + "existed": true + }, + { + "op": "put", + "key": "u/￿/😀", + "value": 1152921504606847488 + }, + { + "op": "commit", + "root": "8a683b0870d647eaf4beea9889c447ec7dbabc9f5f1bd884e66bea992cd1de37" + }, + { + "op": "commit", + "root": "8a683b0870d647eaf4beea9889c447ec7dbabc9f5f1bd884e66bea992cd1de37" + }, + { + "op": "put", + "key": "x", + "value": -7063685366348132 + }, + { + "op": "put", + "key": "abc/abc", + "value": "bc
19" + }, + { + "op": "delete", + "key": "user/x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": "cy 中
9" + }, + { + "op": "put", + "key": "😀", + "value": null + }, + { + "op": "delete", + "key": "u/a/x", + "existed": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "user/users/😀", + "value": 1152921504606847794 + }, + { + "op": "put", + "key": "user/￿/users", + "value": [ + 7263133179168950, + -534321.9471758215, + "0b\"\t/￿\"" + ] + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "users", + "value": 8.353777129648925e-10 + }, + { + "op": "put", + "key": "😀", + "value": [ + 1152921504606847377, + -0.6249474185050428 + ] + }, + { + "op": "delete", + "key": "x/users/u", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": { + "/c
\n0": [ + "é\u0001￿😀\t￿\u0001z", + 5753815969464206, + "😀\\￿中" + ], + "9 a￿": null, + "\t\"1é\t0\u0001": 1152921504606847688, + "\nb
x\"a": "b \\a// 😀" + } + }, + { + "op": "commit", + "root": "5cbb7f79d48a4196f63435d4d4e73b10a66babc0663b4d55493c2fd04768806d" + }, + { + "op": "delete", + "key": "😀/￿/user", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": 3436522652239250 + }, + { + "op": "put", + "key": "a/users", + "value": 4795755137747556 + }, + { + "op": "delete", + "key": "😀/abc/é", + "existed": true + }, + { + "op": "put", + "key": "￿/😀", + "value": false + }, + { + "op": "put", + "key": "x", + "value": 45.521129922139956 + }, + { + "op": "put", + "key": "abc/users/ab", + "value": { + "ya": -18446744073709551616, + "\u0001/￿x😀
 中": "a c
\u0001x", + "1￿
9\n\"1b": false + } + }, + { + "op": "put", + "key": "ab", + "value": null + }, + { + "op": "commit", + "root": "a3bb2e8ee3404754d97615153fdcf492ba97032a8936dd5568a93bb337ba5c2d" + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "￿", + "value": 1152921504606847316 + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "￿/ab/a", + "value": -1955416100330730 + }, + { + "op": "commit", + "root": "5fd028b781505a2a521a502d6551a56c0bf13d8e7a39d427273435378f38dda8" + }, + { + "op": "put", + "key": "users/abc", + "value": 1152921504606847392 + }, + { + "op": "put", + "key": "u", + "value": -4955888810519122 + }, + { + "op": "put", + "key": "users", + "value": 1152921504606847875 + }, + { + "op": "put", + "key": "users", + "value": "b😀c\t0" + }, + { + "op": "delete", + "key": "ab", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": false + }, + { + "op": "delete", + "key": "u/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "abc/users/ab", + "existed": true + }, + { + "op": "put", + "key": "x/é/é", + "value": true + }, + { + "op": "put", + "key": "user/x", + "value": true + }, + { + "op": "delete", + "key": "user/users/😀", + "existed": true + }, + { + "op": "put", + "key": "abc/é", + "value": { + "/\\\u0001éa": -18446744073709551616, + " 😀": 874222861798044 + } + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "delete", + "key": "abc/abc", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "8c1a60ff0a9ca11090d82e7c86db5b077dccb5933b05a7966eb570be34e3c13d" + }, + { + "op": "put", + "key": "abc/😀/u", + "value": -3317481339064278 + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "commit", + "root": "bc790739caad7a7114c337b693d34cc9c07320ab8032ff292d8e91853489d680" + }, + { + "op": "delete", + "key": "user/￿/😀", + "existed": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "commit", + "root": "1cfc5051dd4466c94879282284b646402da8c65597f1492d35a09d4165162c84" + }, + { + "op": "delete", + "key": "😀/users/￿", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": null + }, + { + "op": "commit", + "root": "8e63c6497de8035133cc32cde571dfd4df92ef8de8fd12d40b8921f079c62dec" + }, + { + "op": "commit", + "root": "8e63c6497de8035133cc32cde571dfd4df92ef8de8fd12d40b8921f079c62dec" + }, + { + "op": "delete", + "key": "a/users", + "existed": true + }, + { + "op": "put", + "key": "x/é", + "value": -280519.6378761772 + }, + { + "op": "put", + "key": "abc", + "value": [ + -4898538352726972, + true + ] + }, + { + "op": "put", + "key": "x/￿/é", + "value": { + "\u0001zé\"0\nc9": -542121.4288026612, + "\"zb\"c￿1": -825844.6389250358, + "": -0.03927420747133792, + "中\"y\u0001cé/": "😀c\u0001/\n" + } + }, + { + "op": "commit", + "root": "7ff1e39d071e2a5efdd0064bb6aaa741d08eb9232f3b789b5a3f8bf38ffead01" + }, + { + "op": "put", + "key": "abc/users/user", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "😀/é", + "value": null + }, + { + "op": "put", + "key": "u", + "value": { + "9\"
": { + "￿9z/": true, + "\"é😀0\\\t": false, + "b
1": null, + "b": "\u0001😀\u0001/éz😀\u0001" + }, + "a\\中中\tz": -18446744073709551616, + "\t1
中 ": true + } + }, + { + "op": "commit", + "root": "e5c8ad8c347cc2578fe69690270e3f2c370ed6e2bb29b460c5e67fc360fbe758" + }, + { + "op": "put", + "key": "users", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "9709292821d7f2127ac977d6af17ba8299d50c7b4d220b0273802694d624b5c0" + }, + { + "op": "put", + "key": "x", + "value": 0.04996968708111881 + }, + { + "op": "put", + "key": "u/abc", + "value": "z/" + }, + { + "op": "put", + "key": "x/￿/ab", + "value": -0.14518588083877848 + }, + { + "op": "put", + "key": "x/a", + "value": "a/" + }, + { + "op": "put", + "key": "￿/user/x", + "value": { + "\t\"0bcxx中": { + "\\z\u0001\té😀b": "😀\t\u0001" + }, + " \"z￿9z": null, + "bc中1\t/\t/": true + } + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": [ + { + "/y": -6535351210100194, + "10\t": 1152921504606847874, + "x": -0.6965929718411152, + "a\\\u0001\t ": -18446744073709551616 + }, + -18446744073709551616, + -191842873337320 + ] + }, + { + "op": "put", + "key": "u", + "value": [] + }, + { + "op": "put", + "key": "￿/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/user/a", + "value": [ + -0.35811442277877137 + ] + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/abc", + "value": { + "a\\": 1152921504606847594, + "c￿/": { + "x\na\"": null + }, + "yx中z/y": { + "中
é90": true, + "zyx\t😀éy\n": false, + "￿ya\"z": true + } + } + }, + { + "op": "put", + "key": "u/user/￿", + "value": { + "0\"": 7091232726655776, + "c\\\u0001aé": { + "0a ": null + }, + "é0cx": false, + "x\t😀é
": { + "\n\"": -18446744073709551616, + "\n\"中00c": true + } + } + }, + { + "op": "put", + "key": "x/😀", + "value": true + }, + { + "op": "delete", + "key": "a/abc", + "existed": true + }, + { + "op": "put", + "key": "é/user", + "value": 9151900423.560808 + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "delete", + "key": "ab/x", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": { + "": 1152921504606847109 + } + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "commit", + "root": "62accf9d3c3867520e58faab580a6588cdb8e20e7c29e43193f2bb6798873b43" + }, + { + "op": "put", + "key": "￿", + "value": [] + }, + { + "op": "put", + "key": "é/x/￿", + "value": { + "\\\u0001/\txc0😀": 1152921504606847082 + } + }, + { + "op": "put", + "key": "u", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": { + "": { + "\\
b￿": null, + "￿\u0001\t1": 8931341152562429, + "\u0001\t中b9": "\t\t", + "😀😀z/": "\n中￿\\\u0001
" + }, + " ac": 8515762626616835, + "\"": "中 yx", + "y\tb\t\u0001😀/中": 1152921504606847104 + } + }, + { + "op": "put", + "key": "u/😀/ab", + "value": null + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "abc/😀/u", + "existed": true + }, + { + "op": "put", + "key": "x/😀/u", + "value": { + "😀b0zb": false, + "中": [ + true, + true + ], + "cé\"": { + " 😀": true, + "ba\n￿": -457337379954808 + }, + "\ncz0b
": { + "\"éyz1": false, + "": 6623239290182256, + "/019yé\\": null + } + } + }, + { + "op": "put", + "key": "users/😀/ab", + "value": "0y
\\" + }, + { + "op": "delete", + "key": "x/😀/u", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": {} + }, + { + "op": "commit", + "root": "fb0b23152abc5ab7ab0eac27e4b149cf819a8e130c2582b9888770e47ad29140" + }, + { + "op": "delete", + "key": "😀/a/a", + "existed": true + }, + { + "op": "put", + "key": "x/u", + "value": -2549254231391525 + }, + { + "op": "delete", + "key": "é/é/￿", + "existed": false + }, + { + "op": "commit", + "root": "7d75654629105b050116efc139bfec06631e19e444bd1ab7df22be5687c30165" + }, + { + "op": "delete", + "key": "x/😀", + "existed": true + }, + { + "op": "commit", + "root": "cf4f6311699311716df7b4555f06ba9ec75511169d19f6b3d965a362dfde88d7" + }, + { + "op": "put", + "key": "ab", + "value": { + "
x0￿9\u0001": { + "x/y中中9a中": 1152921504606847726, + "\t9x y": null + }, + "\\a\u0001😀y中": { + "éa\\": null, + "￿c1b\tc": -8294236355241357, + "bccb😀": -3460883124835219 + }, + "中": { + "/z
\"é": true, + "1
0": 8872401519835205, + "中/ 😀": null, + "91y1zc9": -0.6700163064224591 + }, + "0zb\t0\\": false + } + }, + { + "op": "put", + "key": "ab/￿/😀", + "value": 1152921504606847061 + }, + { + "op": "put", + "key": "😀/￿/😀", + "value": null + }, + { + "op": "delete", + "key": "é/user", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": "é￿ " + }, + { + "op": "commit", + "root": "12ee732648e74f37f41d69da938e045641bee933590a3fa32fbbc7edaf444a1c" + }, + { + "op": "put", + "key": "users/users", + "value": null + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": true + }, + { + "op": "put", + "key": "x/users/😀", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": null + }, + { + "op": "put", + "key": "user", + "value": 1152921504606847911 + }, + { + "op": "put", + "key": "users/￿", + "value": "9中\\\\é" + }, + { + "op": "put", + "key": "user", + "value": { + "y￿0éaé": null, + "y1/\\b": { + "écby中\n中": 29584211.613887373, + " éa😀90": "中z1c0\u0001zb", + " ayxb😀": -0.649562911066861, + "\\": true + }, + "é\u0001\u0001é\"": { + "\t9\\": 8884777165940577, + "\\￿x": false, + "ca\n😀\u0001": null, + "x

1\t\"é": null + }, + "b9😀é￿": 1152921504606847108 + } + }, + { + "op": "put", + "key": "a/😀", + "value": {} + }, + { + "op": "commit", + "root": "7200c617c537cdc33ee0fbb1837096af4f2c355f080d0f750a7d97d5764660b3" + }, + { + "op": "put", + "key": "x", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "put", + "key": "😀/u", + "value": { + "b": null, + "": "", + " z中\u0001": -18446744073709551616, + "11\"ba": { + "\\yz": true, + "": true, + "中￿ézb\n1": 344532283821597, + "0b￿\\\n\\é": -0.3260019933467111 + } + } + }, + { + "op": "delete", + "key": "u/abc", + "existed": true + }, + { + "op": "commit", + "root": "c6221930fb981dc343aaaa448ab6e5fdb9e90d539f017b93422e89f6f5f95695" + }, + { + "op": "put", + "key": "￿/x/user", + "value": "\u0001 \"\t\\中" + }, + { + "op": "put", + "key": "￿/￿/a", + "value": "" + }, + { + "op": "commit", + "root": "ecd09b19462bfc59332e4ed7d4ac7e66b2f5d3deb799acfbc423e00494e6eddd" + }, + { + "op": "put", + "key": "user/ab", + "value": "az\u0001x1yx" + }, + { + "op": "put", + "key": "u", + "value": { + "ab9bé\u0001
": -0.12433582379621178, + " c1\t中\"": { + "😀9zyb/": -2225304300974304, + "
": "😀zy
x", + "0\u00011\"￿é
z": true + }, + "
\\": { + "b中ca\"": true, + "\té😀9\n￿": -324841581102270, + "c😀￿by": "zcacaz9" + }, + "\t\\中": [ + -18446744073709551616, + "x\\\t1c/" + ] + } + }, + { + "op": "put", + "key": "é/users/ab", + "value": 1152921504606847765 + }, + { + "op": "delete", + "key": "￿/x/user", + "existed": true + }, + { + "op": "put", + "key": "é/users/x", + "value": {} + }, + { + "op": "put", + "key": "users/users/￿", + "value": { + "\\b": 7927229420658014, + "é￿\n
": 1265880572805915 + } + }, + { + "op": "put", + "key": "ab/users/é", + "value": [ + { + "z\t\\😀中": 1152921504606847104 + }, + { + "\u0001b
 z
": -4068212830225738, + "9": null + }, + false, + true + ] + }, + { + "op": "put", + "key": "users", + "value": 1.8686463799365743e-10 + }, + { + "op": "delete", + "key": "x/a", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": 0.1482451034984853 + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "delete", + "key": "abc/é", + "existed": true + }, + { + "op": "put", + "key": "users/users/x", + "value": "\t" + }, + { + "op": "put", + "key": "ab", + "value": [ + "\\x
1 
y", + [ + 1152921504606847937, + 3772187621166608, + -811193.049054771, + 4674140977728952 + ] + ] + }, + { + "op": "delete", + "key": "￿/￿/a", + "existed": true + }, + { + "op": "put", + "key": "users/a/😀", + "value": 1152921504606847632 + }, + { + "op": "put", + "key": "😀/u/ab", + "value": null + }, + { + "op": "put", + "key": "a/a", + "value": -0.515913436172214 + }, + { + "op": "delete", + "key": "x/é", + "existed": true + }, + { + "op": "put", + "key": "u/a", + "value": true + }, + { + "op": "put", + "key": "a/user/a", + "value": "/\"9😀y" + }, + { + "op": "put", + "key": "x/￿", + "value": null + }, + { + "op": "put", + "key": "a", + "value": "c\n\"" + }, + { + "op": "delete", + "key": "u/a", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "é/x/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/￿", + "existed": true + }, + { + "op": "delete", + "key": "users/users", + "existed": true + }, + { + "op": "delete", + "key": "a/x", + "existed": false + }, + { + "op": "put", + "key": "users/u", + "value": [] + }, + { + "op": "put", + "key": "😀/ab/é", + "value": { + "": true, + "\u0001\\\u0001": true, + "\u00010": "b y9
é ", + "y\n/é/09": null + } + }, + { + "op": "put", + "key": "ab/😀/ab", + "value": " 9" + }, + { + "op": "delete", + "key": "ab/😀/ab", + "existed": true + }, + { + "op": "put", + "key": "😀/user", + "value": {} + }, + { + "op": "put", + "key": "users/￿", + "value": "" + }, + { + "op": "put", + "key": "x", + "value": 1152921504606847108 + }, + { + "op": "put", + "key": "é/a/￿", + "value": true + }, + { + "op": "put", + "key": "ab", + "value": 704189.2676369569 + }, + { + "op": "put", + "key": "u/é/ab", + "value": {} + }, + { + "op": "commit", + "root": "1d1ee29fcfd763349037773c4d7b4a573eb80b42e318d00a6b702c7b7ef6d871" + }, + { + "op": "put", + "key": "x/a", + "value": [] + }, + { + "op": "put", + "key": "user/a/￿", + "value": {} + }, + { + "op": "delete", + "key": "users/users/x", + "existed": true + }, + { + "op": "put", + "key": "users/ab", + "value": true + }, + { + "op": "put", + "key": "ab/u", + "value": "" + }, + { + "op": "put", + "key": "a/user/users", + "value": { + "": [ + null, + -18446744073709551616 + ], + "9": 3545523840262856, + "cz😀": { + "": -18446744073709551616, + "b\\": 1152921504606847400, + "1c": "中中/90é￿" + }, + "\\1 ￿": "
9" + } + }, + { + "op": "put", + "key": "a/a/￿", + "value": null + }, + { + "op": "put", + "key": "abc/a/user", + "value": null + }, + { + "op": "delete", + "key": "u/user/￿", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": -52867956797762 + }, + { + "op": "commit", + "root": "97906b9cb970f33f1100628aaa48403679598db42883bcc930c8832b5b90943e" + }, + { + "op": "put", + "key": "abc/x", + "value": -694680.7535715397 + }, + { + "op": "put", + "key": "😀", + "value": "" + }, + { + "op": "put", + "key": "x/😀/é", + "value": { + "￿\u00010\n\\": "c", + "/\"": { + "\\": null, + "￿0x1\u0001\\": false, + "￿x\"": -1090520089078268 + }, + "\\
": { + "bé": false, + "x9": "", + "": 5979433220707924, + "b y": true + } + } + }, + { + "op": "put", + "key": "u", + "value": 47507468532721.555 + }, + { + "op": "delete", + "key": "a/user/a", + "existed": true + }, + { + "op": "delete", + "key": "a/user/users", + "existed": true + }, + { + "op": "delete", + "key": "abc/users/user", + "existed": true + }, + { + "op": "delete", + "key": "ab/￿/😀", + "existed": true + }, + { + "op": "put", + "key": "abc/u/abc", + "value": [ + 1205982392274335, + { + "😀中1y中😀x\n": -18446744073709551616, + "\t中\\": 1210956020351507, + "bb": 2144588251547270 + } + ] + }, + { + "op": "put", + "key": "x/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x/a", + "value": -5037139323945801 + }, + { + "op": "put", + "key": "a", + "value": { + " \u0001b": { + "": -3613743620030602 + }, + "\na\t": 1152921504606847405, + "z": [], + "zaé\u0001\u0001\"": [ + null, + -18446744073709551616, + 1.0730825888967211e-10 + ] + } + }, + { + "op": "commit", + "root": "8127269bc94243125f4f3eeb5a1b23fd8d81aee0be143d19890fc7c1b43a6da2" + }, + { + "op": "delete", + "key": "😀/user/x", + "existed": true + }, + { + "op": "commit", + "root": "9ee865231e874b662259fb79a2a6525ff1eb91a1b6218d5a9257c11a8f61229e" + }, + { + "op": "put", + "key": "user/user", + "value": "\\céxc\\\u0001\"" + }, + { + "op": "put", + "key": "😀", + "value": 9791490748.585184 + }, + { + "op": "delete", + "key": "x/￿/a", + "existed": false + }, + { + "op": "delete", + "key": "users/users/x", + "existed": false + }, + { + "op": "put", + "key": "user/😀/￿", + "value": true + }, + { + "op": "put", + "key": "user/abc/abc", + "value": { + "\tc\\\"a ": 168879.26095807785 + } + }, + { + "op": "put", + "key": "ab/user", + "value": "//0" + }, + { + "op": "commit", + "root": "a531f2c57935aecf85f0cf284377bb3475990d5c57adc955331eebf9c8828ac9" + }, + { + "op": "put", + "key": "user", + "value": { + "": "9c
1ay\n\"", + "\u0001b\\x": "11é😀\n😀" + } + }, + { + "op": "put", + "key": "user", + "value": { + "c￿1中
": [ + true + ] + } + }, + { + "op": "delete", + "key": "user/abc/abc", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": -8405007150166980 + }, + { + "op": "put", + "key": "users/user", + "value": null + }, + { + "op": "put", + "key": "é/é/ab", + "value": -1331536963664203 + }, + { + "op": "delete", + "key": "user/x", + "existed": true + }, + { + "op": "put", + "key": "users/ab", + "value": 390423.53405178036 + }, + { + "op": "put", + "key": "a/user/é", + "value": { + "z0zc": 1152921504606847331 + } + }, + { + "op": "put", + "key": "a/a/abc", + "value": [ + {}, + { + "zz￿x\"": false, + "￿c\t\n0cc": -18446744073709551616, + "\\\"😀/\n": "0", + "\"é\u0001\"x\t": -4949069302991004 + }, + "y09é￿b", + 1442236461640640 + ] + }, + { + "op": "put", + "key": "ab", + "value": false + }, + { + "op": "delete", + "key": "x/a", + "existed": true + }, + { + "op": "commit", + "root": "b2278d1c39ab4ae1a86d23ade7fa01bb5e1c000bfc05b5776316768cb24a92ef" + } + ], + "items": [ + [ + "a", + { + "\na\t": 1152921504606847405, + " \u0001b": { + "": -3613743620030602 + }, + "z": [], + "zaé\u0001\u0001\"": [ + null, + -18446744073709551616, + 1.0730825888967211e-10 + ] + } + ], + [ + "a/a", + -0.515913436172214 + ], + [ + "a/a/abc", + [ + {}, + { + "\"é\u0001\"x\t": -4949069302991004, + "\\\"😀/\n": "0", + "zz￿x\"": false, + "￿c\t\n0cc": -18446744073709551616 + }, + "y09é￿b", + 1442236461640640 + ] + ], + [ + "a/a/￿", + null + ], + [ + "a/u/abc", + "" + ], + [ + "a/user/é", + { + "z0zc": 1152921504606847331 + } + ], + [ + "a/é/￿", + 3895616142526731 + ], + [ + "a/😀", + {} + ], + [ + "ab", + false + ], + [ + "ab/u", + "" + ], + [ + "ab/user", + "//0" + ], + [ + "ab/users/é", + [ + { + "z\t\\😀中": 1152921504606847104 + }, + { + "\u0001b
 z
": -4068212830225738, + "9": null + }, + false, + true + ] + ], + [ + "abc", + -18446744073709551616 + ], + [ + "abc/a/user", + null + ], + [ + "abc/u/abc", + [ + 1205982392274335, + { + "\t中\\": 1210956020351507, + "bb": 2144588251547270, + "😀中1y中😀x\n": -18446744073709551616 + } + ] + ], + [ + "abc/x", + -694680.7535715397 + ], + [ + "u", + -8405007150166980 + ], + [ + "u/é/ab", + {} + ], + [ + "u/￿/😀", + 1152921504606847488 + ], + [ + "u/😀/ab", + null + ], + [ + "user", + { + "c￿1中
": [ + true + ] + } + ], + [ + "user/a/￿", + {} + ], + [ + "user/ab", + "az\u0001x1yx" + ], + [ + "user/user", + "\\céxc\\\u0001\"" + ], + [ + "user/￿/users", + [ + 7263133179168950, + -534321.9471758215, + "0b\"\t/￿\"" + ] + ], + [ + "user/😀/￿", + true + ], + [ + "users", + -52867956797762 + ], + [ + "users/a/😀", + 1152921504606847632 + ], + [ + "users/ab", + 390423.53405178036 + ], + [ + "users/abc", + 1152921504606847392 + ], + [ + "users/u", + [] + ], + [ + "users/user", + null + ], + [ + "users/users/￿", + { + "\\b": 7927229420658014, + "é￿\n
": 1265880572805915 + } + ], + [ + "users/￿", + "" + ], + [ + "users/😀/ab", + "0y
\\" + ], + [ + "x", + 1152921504606847108 + ], + [ + "x/abc", + -18446744073709551616 + ], + [ + "x/u", + -2549254231391525 + ], + [ + "x/users/😀", + null + ], + [ + "x/é/é", + true + ], + [ + "x/￿", + null + ], + [ + "x/￿/ab", + -0.14518588083877848 + ], + [ + "x/￿/é", + { + "": -0.03927420747133792, + "\u0001zé\"0\nc9": -542121.4288026612, + "\"zb\"c￿1": -825844.6389250358, + "中\"y\u0001cé/": "😀c\u0001/\n" + } + ], + [ + "x/😀/é", + { + "/\"": { + "\\": null, + "￿0x1\u0001\\": false, + "￿x\"": -1090520089078268 + }, + "\\
": { + "": 5979433220707924, + "b y": true, + "bé": false, + "x9": "" + }, + "￿\u00010\n\\": "c" + } + ], + [ + "é", + -18446744073709551616 + ], + [ + "é/a/￿", + true + ], + [ + "é/users/ab", + 1152921504606847765 + ], + [ + "é/users/x", + {} + ], + [ + "é/é/ab", + -1331536963664203 + ], + [ + "￿", + null + ], + [ + "￿/ab/a", + -1955416100330730 + ], + [ + "￿/abc", + -18446744073709551616 + ], + [ + "￿/user/x", + { + "\t\"0bcxx中": { + "\\z\u0001\té😀b": "😀\t\u0001" + }, + " \"z￿9z": null, + "bc中1\t/\t/": true + } + ], + [ + "￿/😀", + false + ], + [ + "😀", + 9791490748.585184 + ], + [ + "😀/ab/é", + { + "": true, + "\u00010": "b y9
é ", + "\u0001\\\u0001": true, + "y\n/é/09": null + } + ], + [ + "😀/u", + { + "": "", + " z中\u0001": -18446744073709551616, + "11\"ba": { + "": true, + "0b￿\\\n\\é": -0.3260019933467111, + "\\yz": true, + "中￿ézb\n1": 344532283821597 + }, + "b": null + } + ], + [ + "😀/u/ab", + null + ], + [ + "😀/user", + {} + ], + [ + "😀/é", + null + ], + [ + "😀/￿/😀", + null + ] + ] + }, + { + "name": "random-2/sha256", + "addressing": "sha256", + "steps": [ + { + "op": "commit", + "root": null + }, + { + "op": "commit", + "root": null + }, + { + "op": "put", + "key": "user", + "value": "￿0￿b" + }, + { + "op": "put", + "key": "é", + "value": { + "中\té9b": null, + "\t/\\\né": -5810787696403591, + "1az": 1152921504606847153, + "é\"é中z\t\né": -18446744073709551616 + } + }, + { + "op": "put", + "key": "user/é", + "value": { + "\u00019\u0001": [ + 6896831261063722, + 0.09465368642821947, + 7410724576053357, + 62915024.06150362 + ], + "\u0001é\"": false, + "a0xb😀": null, + "😀1xé": -4218229742632357 + } + }, + { + "op": "put", + "key": "é", + "value": null + }, + { + "op": "put", + "key": "users", + "value": true + }, + { + "op": "put", + "key": "users", + "value": 1152921504606847237 + }, + { + "op": "delete", + "key": "user/é", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": null + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "u/ab/abc", + "value": {} + }, + { + "op": "put", + "key": "users/ab/é", + "value": 8264717403952100 + }, + { + "op": "put", + "key": "u/users", + "value": { + "😀\\\u0001é/y/9": "
a中yb9" + } + }, + { + "op": "put", + "key": "u", + "value": { + "b11\tc9c😀": 73854.84734464146 + } + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": true + }, + { + "op": "put", + "key": "a", + "value": true + }, + { + "op": "put", + "key": "￿/é", + "value": 0.5210497969853378 + }, + { + "op": "put", + "key": "é", + "value": [ + -7317684015496443, + { + "": "￿", + " \" a\n": null + }, + true, + null + ] + }, + { + "op": "delete", + "key": "￿/é", + "existed": true + }, + { + "op": "put", + "key": "x/u/users", + "value": "" + }, + { + "op": "put", + "key": "a", + "value": { + "😀": -18446744073709551616 + } + }, + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "commit", + "root": "037b9ae75f616f951be10d32f5f5f2b90d0c075b9885e105876c0605cda2b2ed" + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "put", + "key": "😀", + "value": "\ty\"9\u0001" + }, + { + "op": "put", + "key": "é/😀/ab", + "value": { + "\u0001￿": "\nc😀😀xc\"z" + } + }, + { + "op": "commit", + "root": "eb693734513376c73f0745b3fdf071fd3a3f2239128f52f556800595c95ee617" + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "put", + "key": "abc", + "value": { + " xy中\nx/é": 1264464186784.523 + } + }, + { + "op": "put", + "key": "ab/é", + "value": "
\u0001\"中/c" + }, + { + "op": "delete", + "key": "a/é/é", + "existed": false + }, + { + "op": "commit", + "root": "27fe864cbb0c43dfe3a1684135fd573e2b55b9011cedc4f6e0b039cc66e2db34" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "user/u", + "value": null + }, + { + "op": "put", + "key": "x/é/a", + "value": true + }, + { + "op": "put", + "key": "x/x", + "value": 0.22385174241472972 + }, + { + "op": "put", + "key": "a/u", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x", + "value": { + "9é\tac￿\"": 4508207094872803, + "9
ybz\u0001": { + "ya 中": 5841994782166.965, + "0 \u0001
y\u0001中": -18446744073709551616, + "9": -2995669663086030 + } + } + }, + { + "op": "delete", + "key": "x/x", + "existed": true + }, + { + "op": "put", + "key": "a/é/x", + "value": [ + [] + ] + }, + { + "op": "put", + "key": "￿/a", + "value": [ + null, + true + ] + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "😀/abc", + "value": -1044675643202406 + }, + { + "op": "commit", + "root": "49e25b96b815a58fbdbe3f4d90b18169bc49a707b951fc54697cea3369768e55" + }, + { + "op": "put", + "key": "😀", + "value": -0.3475467091950055 + }, + { + "op": "put", + "key": "user/abc", + "value": -6568388855102268 + }, + { + "op": "put", + "key": "é/é/abc", + "value": "😀/\\
 中￿
" + }, + { + "op": "delete", + "key": "x/u/users", + "existed": true + }, + { + "op": "put", + "key": "😀/😀", + "value": false + }, + { + "op": "put", + "key": "users", + "value": true + }, + { + "op": "put", + "key": "é/users/￿", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a", + "value": 1152921504606847233 + }, + { + "op": "put", + "key": "a/a", + "value": 456073.1144339554 + }, + { + "op": "put", + "key": "😀", + "value": "/é" + }, + { + "op": "put", + "key": "x", + "value": { + "": { + "￿/中￿￿c😀": "\u0001\\\tz\n\\é\t", + "": null, + "😀yxézc\\": 5366988803890382 + } + } + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": {} + }, + { + "op": "put", + "key": "users/😀", + "value": -0.6007859178460306 + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": { + "": 68284553343612.805 + } + }, + { + "op": "put", + "key": "user/￿/u", + "value": 8235722742674778 + }, + { + "op": "put", + "key": "abc/é", + "value": -6658090824912385 + }, + { + "op": "put", + "key": "￿", + "value": true + }, + { + "op": "put", + "key": "a/é", + "value": 847266905937133 + }, + { + "op": "delete", + "key": "users/ab/é", + "existed": true + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "users/ab/😀", + "value": [] + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": [ + false + ] + }, + { + "op": "commit", + "root": "c29c2fe9258d5e7dd2e443144a1df0762c8ac790a40b41244de0d0438634e7f0" + }, + { + "op": "put", + "key": "user", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "a/u", + "existed": true + }, + { + "op": "put", + "key": "users/😀/ab", + "value": false + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "x/￿", + "value": -0.742495377461639 + }, + { + "op": "commit", + "root": "02e13f8596165275cdd9615c5c85fc814b463695ebf36a82a4585f2539c7a98b" + }, + { + "op": "put", + "key": "a/x/😀", + "value": { + "😀/ cz\"￿": { + " 0": "xaa0" + } + } + }, + { + "op": "commit", + "root": "3fa77e18d25950cf440b68bf6c0b93e5f8f034bd9ea763414e8a41dc0b9bcf1f" + }, + { + "op": "put", + "key": "abc/😀/u", + "value": { + "": "0y", + "￿\\": false, + "9
c\u0001\u00011y": "0￿/" + } + }, + { + "op": "put", + "key": "é/a/a", + "value": [] + }, + { + "op": "put", + "key": "é/é/u", + "value": { + "\u0001\\ 011": null, + "éc￿中ab\\\n": { + "9x\"é\"é\u0001😀": null + }, + "19aa\u0001by": -3212338297784968 + } + }, + { + "op": "put", + "key": "user", + "value": "
" + }, + { + "op": "put", + "key": "user", + "value": { + "": "9\u0001b中\"/x￿" + } + }, + { + "op": "put", + "key": "abc/ab", + "value": { + "￿yab/\n
a": 1152921504606847664, + "中": [ + -142772.81732244696, + "z ab1", + "\\c\"", + null + ] + } + }, + { + "op": "commit", + "root": "870f9da46b2294e2e9d846490676ecf9039d97efbf78776c9db2bc8071bc0640" + }, + { + "op": "commit", + "root": "870f9da46b2294e2e9d846490676ecf9039d97efbf78776c9db2bc8071bc0640" + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "a/users/￿", + "value": [ + { + "y\n": true, + "b😀zé\t": 198487.23636563774, + "￿y\"ya9z": false + }, + "\u0001\t0\n\n91", + 1152921504606847744, + { + "": 0.6799907687918971, + "91\t\t": 6252070170451755, + "1中中z\t \"": -780526.7944130792, + "￿\t￿é\tc": -18446744073709551616 + } + ] + }, + { + "op": "put", + "key": "abc/￿/abc", + "value": -4802163647997278 + }, + { + "op": "delete", + "key": "é/a/a", + "existed": true + }, + { + "op": "put", + "key": "user/é/ab", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "abc/users", + "value": { + "": -0.7713840304213011, + "\\z/cé": { + "\tx￿": null, + "a \u0001yb": null, + "\\\u0001
￿a": 277760.69799884036 + }, + " \\😀￿\u0001": { + "é": false, + "\"\"z": false + }, + "0😀\t": -204132.43975000083 + } + }, + { + "op": "put", + "key": "user/a", + "value": -279452717319622 + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "commit", + "root": "7445c63dce7755c735719d87b0e41b04a77e69559dfcfa44a27ad78cbf69027c" + }, + { + "op": "commit", + "root": "7445c63dce7755c735719d87b0e41b04a77e69559dfcfa44a27ad78cbf69027c" + }, + { + "op": "put", + "key": "a/user", + "value": "￿aa\"x " + }, + { + "op": "put", + "key": "u/u", + "value": { + "\t
": true + } + }, + { + "op": "commit", + "root": "365923bdc3351189fc26ffb2e892e4e250b946e7b051d4372dc89712ada4dd1c" + }, + { + "op": "put", + "key": "ab/ab/users", + "value": [ + 0.8224996794113091, + 714451.1198869103, + 706927.5322352627, + 7602748445201485 + ] + }, + { + "op": "put", + "key": "u/abc", + "value": false + }, + { + "op": "commit", + "root": "07a7198a2dd5d3858ca317b3364450b75a28ba4e69bb0a2583fff2dfe3e4d2ea" + }, + { + "op": "commit", + "root": "07a7198a2dd5d3858ca317b3364450b75a28ba4e69bb0a2583fff2dfe3e4d2ea" + }, + { + "op": "commit", + "root": "07a7198a2dd5d3858ca317b3364450b75a28ba4e69bb0a2583fff2dfe3e4d2ea" + }, + { + "op": "delete", + "key": "users/ab/😀", + "existed": true + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "delete", + "key": "user/a", + "existed": true + }, + { + "op": "delete", + "key": "￿/a", + "existed": true + }, + { + "op": "delete", + "key": "é/é/abc", + "existed": true + }, + { + "op": "commit", + "root": "4e7b284109bf0d895dc713116468ed95bad5b551dd57519fe1401f0e86aa0446" + }, + { + "op": "delete", + "key": "u/u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "delete", + "key": "user/ab/x", + "existed": false + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": false + }, + { + "op": "delete", + "key": "a/users/￿", + "existed": true + }, + { + "op": "delete", + "key": "😀/abc", + "existed": true + }, + { + "op": "delete", + "key": "abc/é", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": 8136182827453579 + }, + { + "op": "put", + "key": "a/ab/x", + "value": [] + }, + { + "op": "put", + "key": "x/user/a", + "value": false + }, + { + "op": "put", + "key": "😀", + "value": { + "/": 1.1641864334751701e-05, + "\"中1/": { + "": true + }, + "z\\": -7127687235979652 + } + }, + { + "op": "delete", + "key": "é/users/￿", + "existed": true + }, + { + "op": "put", + "key": "u/users/a", + "value": {} + }, + { + "op": "put", + "key": "user/user", + "value": "9😀1é" + }, + { + "op": "commit", + "root": "6eb88bd89d2a795ff56ae2fb3d59eadfc9e9a3c3600d05a0777fd6e7f8326877" + }, + { + "op": "put", + "key": "😀/é", + "value": "z
\n 😀\t" + }, + { + "op": "put", + "key": "￿/u", + "value": null + }, + { + "op": "put", + "key": "😀/u/￿", + "value": [ + [ + false, + -7095750625121347 + ], + 9.745363582635186, + { + " ": -717185.8436805888 + } + ] + }, + { + "op": "commit", + "root": "21312237733c6ce53c1fefedf5217868168438af2ebfc89da49baab9b71802da" + }, + { + "op": "put", + "key": "é", + "value": [ + 8.123583920255413, + 3788.350462644594 + ] + }, + { + "op": "put", + "key": "￿/users", + "value": null + }, + { + "op": "delete", + "key": "user/abc", + "existed": true + }, + { + "op": "commit", + "root": "1021665dab3314d615ed197e0b890db2ba2e01039279dce620108ad5a50557f3" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": "9é9b/a\n" + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "u/user", + "value": null + }, + { + "op": "put", + "key": "x/user/users", + "value": null + }, + { + "op": "put", + "key": "x/￿/😀", + "value": { + "xac0a": [ + null, + true, + -8681219311921926, + 1152921504606847576 + ], + "9x\\
z": {}, + "0z￿": { + "": false, + "z￿a": false, + "
1 ": -7503386500974739 + }, + "
y😀": 977924.572013817 + } + }, + { + "op": "put", + "key": "a", + "value": " 中 b " + }, + { + "op": "commit", + "root": "78b849293d0d9b53efa084c50526ded9c3124aad28af23e838a1d97d56f41bad" + }, + { + "op": "commit", + "root": "78b849293d0d9b53efa084c50526ded9c3124aad28af23e838a1d97d56f41bad" + }, + { + "op": "put", + "key": "x", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "23039eaff156d150ef59b315d19271beaf0de15fab121eb58944e13aefb32941" + }, + { + "op": "put", + "key": "￿", + "value": 1884570001370224 + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "delete", + "key": "ab/é", + "existed": true + }, + { + "op": "put", + "key": "user/é/a", + "value": true + }, + { + "op": "put", + "key": "x", + "value": "/abax0 " + }, + { + "op": "put", + "key": "a/a/a", + "value": true + }, + { + "op": "put", + "key": "x", + "value": { + "": null, + "x0": 0.4279955369099355, + "z": "￿cz" + } + }, + { + "op": "delete", + "key": "😀/users", + "existed": false + }, + { + "op": "delete", + "key": "abc/😀/u", + "existed": true + }, + { + "op": "delete", + "key": "a/user", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": -831238.3231132485 + }, + { + "op": "put", + "key": "é/a", + "value": 20162852062012.543 + }, + { + "op": "commit", + "root": "d008971bc6e884a126103a7763f7c230f754fbf6134b2da3fdbe2f99e20ee6d1" + }, + { + "op": "delete", + "key": "a/a/a", + "existed": true + }, + { + "op": "put", + "key": "a/￿/é", + "value": [] + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "é/a", + "value": 3429741327020023 + }, + { + "op": "put", + "key": "users", + "value": "y\\\n😀by
b" + }, + { + "op": "put", + "key": "ab/￿/user", + "value": 8975440110461416 + }, + { + "op": "put", + "key": "é", + "value": [ + -1480753958253816, + "a
0", + "", + 0.9184893929983005 + ] + }, + { + "op": "put", + "key": "￿/a/é", + "value": true + }, + { + "op": "put", + "key": "user/ab/x", + "value": 816313151259329 + }, + { + "op": "commit", + "root": "be03c17dd1d264530000e2aa05d6f8d23339c4c7e4dcd7154683b3a828c7af52" + }, + { + "op": "put", + "key": "users", + "value": "/￿￿😀
y" + }, + { + "op": "put", + "key": "abc/abc/users", + "value": 4.252731372064616e-12 + }, + { + "op": "put", + "key": "users/x/é", + "value": true + }, + { + "op": "put", + "key": "ab/abc", + "value": { + "0/中a￿a￿": true, + "\n￿\t😀9\n": null + } + }, + { + "op": "put", + "key": "user", + "value": 1152921504606847197 + }, + { + "op": "commit", + "root": "b1e2018a9643211c83d92cd3f625878e1c72a2276bb48b2e46cf10ce158d7570" + }, + { + "op": "put", + "key": "ab/abc", + "value": null + }, + { + "op": "put", + "key": "user/user", + "value": "\u0001é中中" + }, + { + "op": "put", + "key": "x/￿/😀", + "value": { + "\\": false, + "yx/\\\u0001😀c\n": { + "\u0001a中\u0001": "
\n0￿c", + "": false, + "a b\n": 3657686048913665 + }, + "\né9": { + "a9z ": null, + "a0z": -4006062950343854, + "/\\😀": true + } + } + }, + { + "op": "put", + "key": "é", + "value": "90" + }, + { + "op": "commit", + "root": "0be16cc038bcba7effeef843644b55d385ef897ba291aeab38d014828fc1891a" + }, + { + "op": "commit", + "root": "0be16cc038bcba7effeef843644b55d385ef897ba291aeab38d014828fc1891a" + }, + { + "op": "delete", + "key": "user/é/a", + "existed": true + }, + { + "op": "put", + "key": "￿", + "value": { + "9￿1\n\t1
": { + "\u0001\\a": -4500260163213891, + "/0😀
": -0.8120118552736082 + }, + "中1
0": true, + "é\t/é": -5601067476714478 + } + }, + { + "op": "put", + "key": "😀/😀", + "value": [ + "\t\t", + null + ] + }, + { + "op": "commit", + "root": "4c5fff1da7f1405c6d023119a343972d3aa35bfd1d27602457bd7fa952ea257b" + }, + { + "op": "delete", + "key": "abc/users", + "existed": true + }, + { + "op": "put", + "key": "😀/user", + "value": "
\nbz\"" + }, + { + "op": "delete", + "key": "x/user/users", + "existed": true + }, + { + "op": "commit", + "root": "4bd12ca069f5930348a2b1a68889e50470f095f42e0f3fa445199190ad79d5a0" + }, + { + "op": "put", + "key": "é/abc/￿", + "value": 79851996.01838501 + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "😀/users/￿", + "value": -0.8636252350646845 + }, + { + "op": "put", + "key": "é/abc", + "value": 7981756839298357 + }, + { + "op": "delete", + "key": "é/abc", + "existed": true + }, + { + "op": "delete", + "key": "user/user", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": 1152921504606847204 + }, + { + "op": "put", + "key": "é", + "value": false + }, + { + "op": "put", + "key": "é", + "value": 0.00029552700270127027 + }, + { + "op": "put", + "key": "a", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "😀", + "value": [ + [ + null, + null, + "y/中1111\n", + -18446744073709551616 + ], + -3422922777074208 + ] + }, + { + "op": "delete", + "key": "￿/u", + "existed": true + }, + { + "op": "delete", + "key": "￿/x", + "existed": false + }, + { + "op": "put", + "key": "￿", + "value": { + "b\n\u0001\\c": true, + "bz1\"\\/￿": -18446744073709551616, + "\t😀\n\nyé￿y": 811883.0582577325, + "c\\中\tz/": { + "": null + } + } + }, + { + "op": "put", + "key": "￿", + "value": "xx" + }, + { + "op": "put", + "key": "u", + "value": 1152921504606847016 + }, + { + "op": "put", + "key": "￿/user/abc", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": 975825.6783753687 + }, + { + "op": "put", + "key": "😀/￿/users", + "value": 1298350660019815 + }, + { + "op": "put", + "key": "x/users", + "value": {} + }, + { + "op": "put", + "key": "😀/u/a", + "value": {} + }, + { + "op": "commit", + "root": "9f8dbb0501b6f10fdafe85f61402e17d5203aa77a0ea74a2c395245e5d28f1c1" + }, + { + "op": "delete", + "key": "ab/abc", + "existed": true + }, + { + "op": "put", + "key": "x/😀", + "value": { + "y￿￿ ": { + "x": null, + "x/b a0": "\"
" + }, + "
yc9￿": false, + "😀1": null + } + }, + { + "op": "commit", + "root": "87b4b31ad31c2d68ce230291cc4f804b1a928a15e8fa6af6e9a970e7debe8d19" + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "ab", + "value": { + "😀b": -199200.11279452336, + "/c 0cc": -18446744073709551616, + "ay
\"\" \u0001é": null + } + }, + { + "op": "put", + "key": "￿/x", + "value": -4457290127836679 + }, + { + "op": "commit", + "root": "3a2aa51cd91e8eff7f3d8f660d41336b7a9d24fca7f5f785df433119fab06eab" + }, + { + "op": "put", + "key": "user/é", + "value": {} + }, + { + "op": "commit", + "root": "ab868f4ec359b346390f245c843b4880557547a72d67be1e457b086a7bcb4716" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "commit", + "root": "281fb35af44134035bc1c37412a62149769357e6615c76920eeab82091cda8f1" + }, + { + "op": "put", + "key": "é/ab", + "value": true + }, + { + "op": "delete", + "key": "😀/😀", + "existed": true + }, + { + "op": "delete", + "key": "😀/￿/users", + "existed": true + }, + { + "op": "delete", + "key": "￿/é", + "existed": false + }, + { + "op": "delete", + "key": "😀/u/a", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": false + }, + { + "op": "delete", + "key": "u/ab/abc", + "existed": true + }, + { + "op": "delete", + "key": "😀/u/￿", + "existed": true + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "delete", + "key": "x/￿/😀", + "existed": true + }, + { + "op": "delete", + "key": "😀/users/￿", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": [ + 683501.1972635924, + {}, + 0.19109490539973928 + ] + }, + { + "op": "put", + "key": "users/abc/x", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x", + "value": [ + {} + ] + }, + { + "op": "delete", + "key": "￿/x", + "existed": true + }, + { + "op": "commit", + "root": "d5ca38a3c8b222e4ae704461b9a71839d7e29cd2880a7776eae66cc078e6b3f7" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "x/abc", + "value": "中中" + }, + { + "op": "put", + "key": "abc", + "value": [ + { + "zcy ": "\"中\u0001c￿9", + " x中😀9 x": "￿x￿\n

ba", + "
0/0\t中": 6430599794759789, + "/\"\\/a中é": -18446744073709551616 + }, + 0.6293664603917879, + 58024990733594.93, + { + "/a\\\n
 ": null + } + ] + }, + { + "op": "put", + "key": "ab/ab/users", + "value": null + }, + { + "op": "commit", + "root": "681ca1a32b1ecc7af0d441c83c912e89ccfe0ed8b0e177a1d2f53aee02062360" + }, + { + "op": "put", + "key": "é/é", + "value": 0.9885952857249212 + }, + { + "op": "put", + "key": "a", + "value": { + "中bxc\n \n/": "0
中\nc
x0", + "": {}, + "￿z￿😀c\"": { + "￿😀\\9a0a": 0.10172587710287129, + "
\n\"z0 \\": 27745454571002.375, + "中y\u0001中é": false, + "\"": "" + } + } + }, + { + "op": "commit", + "root": "8afb8c739e64842ab61fae8dcc1bbe9e79cda9fe2a0a37d56d6ce8db52bd6bee" + }, + { + "op": "commit", + "root": "8afb8c739e64842ab61fae8dcc1bbe9e79cda9fe2a0a37d56d6ce8db52bd6bee" + }, + { + "op": "put", + "key": "😀", + "value": "xx😀/a\u0001😀" + }, + { + "op": "put", + "key": "a", + "value": "z0\\9中😀￿" + }, + { + "op": "put", + "key": "ab/é", + "value": "\"90
😀\\z" + }, + { + "op": "put", + "key": "😀", + "value": { + "
中\u0001a0": "" + } + }, + { + "op": "commit", + "root": "73440078142925d8edfd0fc388338331da0149fa0d7424b1a3539066a19a51dc" + }, + { + "op": "put", + "key": "user", + "value": true + }, + { + "op": "put", + "key": "users", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/é/ab", + "value": -0.8878078883743652 + }, + { + "op": "put", + "key": "user", + "value": { + "z\\\\1": 617485.7139944278, + "": false, + "z
1y\"
b": 1152921504606847838 + } + }, + { + "op": "put", + "key": "a", + "value": "a9" + }, + { + "op": "put", + "key": "😀/user/é", + "value": -8402171115934331 + }, + { + "op": "put", + "key": "abc", + "value": 5583860529011803 + }, + { + "op": "commit", + "root": "f38c29521e0b4bb6f5c88414e410823be309bb632e8febf9057c4fa8bc9f0d5e" + }, + { + "op": "put", + "key": "user/￿", + "value": [] + }, + { + "op": "delete", + "key": "￿/user/abc", + "existed": true + }, + { + "op": "commit", + "root": "0ad1ebfa3f9ec886ecf09faa412b8419f72b7636a7b9b9c9e6dc8156322ad38b" + }, + { + "op": "commit", + "root": "0ad1ebfa3f9ec886ecf09faa412b8419f72b7636a7b9b9c9e6dc8156322ad38b" + }, + { + "op": "put", + "key": "x/u", + "value": 3745147572404717 + }, + { + "op": "put", + "key": "a", + "value": { + "0\\": [ + null, + -206589143784449 + ], + "a\n ": "" + } + }, + { + "op": "delete", + "key": "u/abc", + "existed": true + }, + { + "op": "delete", + "key": "a/x/😀", + "existed": true + }, + { + "op": "commit", + "root": "63d924e85416e06acc5e71bfb8de837a8321623c62e6cb1b6e93bb4dbcffa2f4" + }, + { + "op": "delete", + "key": "user/￿/u", + "existed": true + }, + { + "op": "commit", + "root": "8d00d046545cfce23936346365de038bbcff3c0d3f458890e561836b38aa0149" + }, + { + "op": "delete", + "key": "u/ab", + "existed": false + }, + { + "op": "put", + "key": "abc", + "value": "yc😀" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "delete", + "key": "users/x/é", + "existed": true + }, + { + "op": "commit", + "root": "d241adbbc6c763bcd49aae08e5e368a333c3a0ad4e40d9f1349234ba0eea2400" + }, + { + "op": "put", + "key": "é", + "value": 1152921504606847920 + }, + { + "op": "delete", + "key": "users/😀", + "existed": true + }, + { + "op": "put", + "key": "￿/😀", + "value": -0.3082166045842658 + }, + { + "op": "put", + "key": "x/ab", + "value": [ + -18446744073709551616 + ] + }, + { + "op": "put", + "key": "x/user/user", + "value": false + }, + { + "op": "commit", + "root": "8cbf0139f1e5c92c87dc40ee4c215c6b1d55324ed4fd86c9612b0f6130ee93f1" + }, + { + "op": "put", + "key": "😀", + "value": "中\"0😀bz/中" + }, + { + "op": "put", + "key": "abc/x", + "value": { + " \n\"": 92.14966227236646 + } + }, + { + "op": "commit", + "root": "c00cd9486e6f8396f1c3516627f629c7cd1af200f67752a723e7e790f0acb3ec" + }, + { + "op": "put", + "key": "x/abc", + "value": "中😀\"a\t" + }, + { + "op": "delete", + "key": "😀/user", + "existed": true + }, + { + "op": "delete", + "key": "abc/abc/users", + "existed": true + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "5e68509ee05276f63cc15c4bea15edf94832d0eb9fe175dcbcb70c53d8802ec4" + }, + { + "op": "delete", + "key": "a/ab/x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": 426159.6239946245 + }, + { + "op": "commit", + "root": "6c0087bf5331af6466d030140434d31844cf4464a2f63ffca50f8079425ce5a9" + }, + { + "op": "delete", + "key": "a/a", + "existed": true + }, + { + "op": "delete", + "key": "a/￿/é", + "existed": true + }, + { + "op": "delete", + "key": "users/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "x/a", + "existed": false + }, + { + "op": "delete", + "key": "é/ab", + "existed": true + }, + { + "op": "put", + "key": "é/￿", + "value": 0.07561877714596606 + }, + { + "op": "put", + "key": "ab/a", + "value": { + "\ncc": true, + "\\\n\tza": { + "\nc\n中z 1": 1152921504606847697 + } + } + }, + { + "op": "delete", + "key": "u/users/a", + "existed": true + }, + { + "op": "put", + "key": "x/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "put", + "key": "😀/user", + "value": -3472000912914411 + }, + { + "op": "put", + "key": "a/a", + "value": 0.06132866827788308 + }, + { + "op": "put", + "key": "a/user/users", + "value": -0.9172011080678681 + }, + { + "op": "delete", + "key": "😀/user", + "existed": true + }, + { + "op": "put", + "key": "￿/￿/abc", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "abc/user", + "value": {} + }, + { + "op": "commit", + "root": "43e727a27a0f4414f21bfb95a62e45bcf0e0ad476999ce670b87f6d2ce7eb07f" + }, + { + "op": "delete", + "key": "user/￿", + "existed": true + }, + { + "op": "commit", + "root": "4490add245a9c03b8b2fb7eee5064cb62be5cd3ac2464ee5205dfbebd789ae70" + }, + { + "op": "put", + "key": "u/user/abc", + "value": 3543116664759.4956 + }, + { + "op": "delete", + "key": "abc/￿/abc", + "existed": true + }, + { + "op": "delete", + "key": "user/é/ab", + "existed": true + }, + { + "op": "put", + "key": "u/a/abc", + "value": [ + false, + "c😀
x" + ] + }, + { + "op": "put", + "key": "u", + "value": { + "
x\"\u0001￿é9": false + } + }, + { + "op": "commit", + "root": "c8b2ca42e71a4c5adef17014af3fe3b9a7da9e58befe03dcb486dbfd2a7a0b0f" + }, + { + "op": "delete", + "key": "abc/user", + "existed": true + }, + { + "op": "put", + "key": "u/é", + "value": {} + }, + { + "op": "put", + "key": "a/users", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "users/x", + "value": [ + -18446744073709551616, + { + "\na1c": 1892193662427921, + "": -471541699076132, + "\u0001
\u0001x\"\"": "1\"xy
\\￿\"" + }, + {}, + [ + null, + true + ] + ] + }, + { + "op": "delete", + "key": "é/é/u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": 0.0699746458055941 + }, + { + "op": "put", + "key": "a/é", + "value": false + }, + { + "op": "delete", + "key": "x/user/user", + "existed": true + }, + { + "op": "delete", + "key": "é/a", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": { + "": null + } + }, + { + "op": "put", + "key": "😀", + "value": { + "a1bc
\u0001": null, + "￿\u000191": {}, + "1中￿": true, + "": [ + "x中9￿", + "", + null, + -7682083297527074 + ] + } + }, + { + "op": "put", + "key": "ab/a/user", + "value": { + "": { + "1": -6778640052511576, + " yzz": null, + "y": 191967.5581819946, + "\n中0": true + }, + "\" c\\x": -7215896155523050, + "\\\u0001\t c": 1152921504606847341 + } + }, + { + "op": "commit", + "root": "f8ab1e054806c7506a7c426a7817280de384ec5a6dbaffa46c9d2fa2b004a560" + }, + { + "op": "delete", + "key": "x/é/a", + "existed": true + }, + { + "op": "put", + "key": "x/é", + "value": false + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "ab/a", + "existed": true + }, + { + "op": "put", + "key": "x/users", + "value": 1152921504606847806 + }, + { + "op": "commit", + "root": "fdc996c3995663ac0d5de35c94622a928152fab7bf1ffd3a451745ec3531087f" + }, + { + "op": "put", + "key": "users", + "value": { + "é": "\n ￿\"中/", + "\u0001\n\n\t": true, + "
": [], + "
b\tzy\"0c": true + } + }, + { + "op": "put", + "key": "￿", + "value": 26437716273043 + }, + { + "op": "delete", + "key": "x/abc", + "existed": true + }, + { + "op": "put", + "key": "a/😀/x", + "value": {} + }, + { + "op": "put", + "key": "😀", + "value": {} + }, + { + "op": "put", + "key": "u/abc/￿", + "value": { + " 
cy": null, + "c\n": -944359.3880934673 + } + }, + { + "op": "delete", + "key": "ab/é", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": { + "/": true, + "éz9y ": null + } + }, + { + "op": "commit", + "root": "df1c4076cd40cd99fb70812fa34a361d12026750db51908bf38394eb3be2aa03" + }, + { + "op": "commit", + "root": "df1c4076cd40cd99fb70812fa34a361d12026750db51908bf38394eb3be2aa03" + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "put", + "key": "😀/ab", + "value": 5798900.91171178 + }, + { + "op": "put", + "key": "a/u", + "value": false + }, + { + "op": "commit", + "root": "b294bdfb396177de4a811e652f1a7165a21fb7b2a4551bbd3a400a20964d6e54" + }, + { + "op": "commit", + "root": "b294bdfb396177de4a811e652f1a7165a21fb7b2a4551bbd3a400a20964d6e54" + }, + { + "op": "put", + "key": "a", + "value": [ + { + "": true, + "/😀": -5655752702209879, + "\u0001\u0001\"\"😀
": -460660283449905 + }, + null, + "a
90b
a
" + ] + }, + { + "op": "put", + "key": "x", + "value": -532135.6385488648 + }, + { + "op": "delete", + "key": "😀/user/é", + "existed": true + }, + { + "op": "put", + "key": "users/x/a", + "value": {} + }, + { + "op": "delete", + "key": "x/user/a", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": { + "/y\u0001\"": "9x￿z/é\"
" + } + }, + { + "op": "delete", + "key": "é/￿", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "commit", + "root": "c393dbc1294c2f6004b870f6435592e62bc4f781ffe8b74f06284de7a7563edf" + }, + { + "op": "put", + "key": "ab", + "value": { + "0": true, + "11": "" + } + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "￿/user/ab", + "existed": false + }, + { + "op": "put", + "key": "😀/x", + "value": {} + }, + { + "op": "put", + "key": "😀", + "value": [] + }, + { + "op": "put", + "key": "x/￿/ab", + "value": [] + }, + { + "op": "put", + "key": "u", + "value": { + "\t9中c y": { + "1中z￿😀a1": 2.237744424848274, + "9
中/": -2847243301517036, + "\t": 1554006749238746 + }, + "\tcc￿": 3166238056711594 + } + }, + { + "op": "put", + "key": "abc", + "value": -336747.62071530614 + }, + { + "op": "put", + "key": "users/é/😀", + "value": [ + -18446744073709551616, + {} + ] + }, + { + "op": "delete", + "key": "😀/x", + "existed": true + }, + { + "op": "delete", + "key": "￿/￿/abc", + "existed": true + }, + { + "op": "delete", + "key": "é/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "😀/é", + "existed": true + }, + { + "op": "delete", + "key": "u/é", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": -6224794831260817 + }, + { + "op": "put", + "key": "😀/￿", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "user/ab/x", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": "\\\tbc￿" + }, + { + "op": "put", + "key": "😀/😀/ab", + "value": { + "zy": -4365457848758776, + "
": "/0\t99", + "0\\9b😀zx": { + "\\": -251140734133170, + "￿": true, + "\u0001écb90￿😀": null, + "": null + }, + "": { + "😀c \n/b\\": -18446744073709551616, + "1x
xb
\u00011": -18446744073709551616, + "￿\"\t 9\\": "\t" + } + } + }, + { + "op": "put", + "key": "😀", + "value": [ + null, + { + "cc\t😀\"bx0": null + } + ] + }, + { + "op": "put", + "key": "users/😀", + "value": -1461530824469002 + }, + { + "op": "delete", + "key": "u/a/abc", + "existed": true + }, + { + "op": "delete", + "key": "ab/￿/user", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": 1152921504606847348 + }, + { + "op": "delete", + "key": "x/u", + "existed": true + }, + { + "op": "put", + "key": "é/users", + "value": { + "": null, + "0x/
\"z\n": "b0\t" + } + }, + { + "op": "put", + "key": "😀", + "value": [ + -18446744073709551616, + false, + true + ] + }, + { + "op": "put", + "key": "a", + "value": [ + 582857.2323208617, + 1020079857923796, + 1152921504606847959 + ] + }, + { + "op": "put", + "key": "😀", + "value": 1152921504606847269 + }, + { + "op": "commit", + "root": "f7eb37f8416d29fa4ef6ffde755ff3ef7654b67a42172e846c354a14a78a96b2" + }, + { + "op": "put", + "key": "é", + "value": 7.442987188463953 + }, + { + "op": "commit", + "root": "a82b6b95f3bf6932c14236eb3f9f9542e5f6ad4d6216cbab8aff42c0c2d6e58d" + } + ], + "items": [ + [ + "a", + [ + 582857.2323208617, + 1020079857923796, + 1152921504606847959 + ] + ], + [ + "a/a", + 0.06132866827788308 + ], + [ + "a/u", + false + ], + [ + "a/user/users", + -0.9172011080678681 + ], + [ + "a/users", + null + ], + [ + "a/é", + false + ], + [ + "a/é/ab", + -0.8878078883743652 + ], + [ + "a/é/x", + [ + [] + ] + ], + [ + "a/😀/x", + {} + ], + [ + "ab", + { + "0": true, + "11": "" + } + ], + [ + "ab/a/user", + { + "": { + "\n中0": true, + " yzz": null, + "1": -6778640052511576, + "y": 191967.5581819946 + }, + "\" c\\x": -7215896155523050, + "\\\u0001\t c": 1152921504606847341 + } + ], + [ + "ab/ab/users", + null + ], + [ + "abc", + -336747.62071530614 + ], + [ + "abc/ab", + { + "中": [ + -142772.81732244696, + "z ab1", + "\\c\"", + null + ], + "￿yab/\n
a": 1152921504606847664 + } + ], + [ + "abc/x", + { + " \n\"": 92.14966227236646 + } + ], + [ + "u", + { + "\t9中c y": { + "\t": 1554006749238746, + "1中z￿😀a1": 2.237744424848274, + "9
中/": -2847243301517036 + }, + "\tcc￿": 3166238056711594 + } + ], + [ + "u/abc/￿", + { + " 
cy": null, + "c\n": -944359.3880934673 + } + ], + [ + "u/user", + null + ], + [ + "u/user/abc", + 3543116664759.4956 + ], + [ + "u/users", + { + "😀\\\u0001é/y/9": "
a中yb9" + } + ], + [ + "user", + 426159.6239946245 + ], + [ + "user/u", + null + ], + [ + "user/é", + {} + ], + [ + "users", + { + "/y\u0001\"": "9x￿z/é\"
" + } + ], + [ + "users/abc/x", + -18446744073709551616 + ], + [ + "users/x", + [ + -18446744073709551616, + { + "": -471541699076132, + "\u0001
\u0001x\"\"": "1\"xy
\\￿\"", + "\na1c": 1892193662427921 + }, + {}, + [ + null, + true + ] + ] + ], + [ + "users/x/a", + {} + ], + [ + "users/é/😀", + [ + -18446744073709551616, + {} + ] + ], + [ + "users/😀", + -1461530824469002 + ], + [ + "x", + -532135.6385488648 + ], + [ + "x/ab", + [ + -18446744073709551616 + ] + ], + [ + "x/users", + 1152921504606847806 + ], + [ + "x/é", + false + ], + [ + "x/￿", + -0.742495377461639 + ], + [ + "x/￿/ab", + [] + ], + [ + "x/😀", + { + "y￿￿ ": { + "x": null, + "x/b a0": "\"
" + }, + "
yc9￿": false, + "😀1": null + } + ], + [ + "é", + 7.442987188463953 + ], + [ + "é/abc/￿", + 79851996.01838501 + ], + [ + "é/users", + { + "": null, + "0x/
\"z\n": "b0\t" + } + ], + [ + "é/é", + 0.9885952857249212 + ], + [ + "￿", + 26437716273043 + ], + [ + "￿/a/é", + true + ], + [ + "￿/users", + null + ], + [ + "￿/😀", + -0.3082166045842658 + ], + [ + "😀", + 1152921504606847269 + ], + [ + "😀/ab", + 5798900.91171178 + ], + [ + "😀/￿", + -18446744073709551616 + ], + [ + "😀/😀/ab", + { + "": { + "1x
xb
\u00011": -18446744073709551616, + "￿\"\t 9\\": "\t", + "😀c \n/b\\": -18446744073709551616 + }, + "0\\9b😀zx": { + "": null, + "\u0001écb90￿😀": null, + "\\": -251140734133170, + "￿": true + }, + "zy": -4365457848758776, + "
": "/0\t99" + } + ] + ] + }, + { + "name": "random-2/swarm", + "addressing": "swarm", + "steps": [ + { + "op": "commit", + "root": null + }, + { + "op": "commit", + "root": null + }, + { + "op": "put", + "key": "user", + "value": "￿0￿b" + }, + { + "op": "put", + "key": "é", + "value": { + "中\té9b": null, + "\t/\\\né": -5810787696403591, + "1az": 1152921504606847153, + "é\"é中z\t\né": -18446744073709551616 + } + }, + { + "op": "put", + "key": "user/é", + "value": { + "\u00019\u0001": [ + 6896831261063722, + 0.09465368642821947, + 7410724576053357, + 62915024.06150362 + ], + "\u0001é\"": false, + "a0xb😀": null, + "😀1xé": -4218229742632357 + } + }, + { + "op": "put", + "key": "é", + "value": null + }, + { + "op": "put", + "key": "users", + "value": true + }, + { + "op": "put", + "key": "users", + "value": 1152921504606847237 + }, + { + "op": "delete", + "key": "user/é", + "existed": true + }, + { + "op": "put", + "key": "x", + "value": null + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "u/ab/abc", + "value": {} + }, + { + "op": "put", + "key": "users/ab/é", + "value": 8264717403952100 + }, + { + "op": "put", + "key": "u/users", + "value": { + "😀\\\u0001é/y/9": "
a中yb9" + } + }, + { + "op": "put", + "key": "u", + "value": { + "b11\tc9c😀": 73854.84734464146 + } + }, + { + "op": "put", + "key": "user", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": true + }, + { + "op": "put", + "key": "a", + "value": true + }, + { + "op": "put", + "key": "￿/é", + "value": 0.5210497969853378 + }, + { + "op": "put", + "key": "é", + "value": [ + -7317684015496443, + { + "": "￿", + " \" a\n": null + }, + true, + null + ] + }, + { + "op": "delete", + "key": "￿/é", + "existed": true + }, + { + "op": "put", + "key": "x/u/users", + "value": "" + }, + { + "op": "put", + "key": "a", + "value": { + "😀": -18446744073709551616 + } + }, + { + "op": "put", + "key": "ab", + "value": true + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "commit", + "root": "12c00ebfee3787a53c6969da41f64cd0d11624f087f61b8f758f4931911e3f8d" + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "put", + "key": "😀", + "value": "\ty\"9\u0001" + }, + { + "op": "put", + "key": "é/😀/ab", + "value": { + "\u0001￿": "\nc😀😀xc\"z" + } + }, + { + "op": "commit", + "root": "5b205214f4d96c174d9b7696efa86415149642fcad06a63967265a861f4b0337" + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "put", + "key": "abc", + "value": { + " xy中\nx/é": 1264464186784.523 + } + }, + { + "op": "put", + "key": "ab/é", + "value": "
\u0001\"中/c" + }, + { + "op": "delete", + "key": "a/é/é", + "existed": false + }, + { + "op": "commit", + "root": "b7e263d9c436d371f7499b21a04af00863adfa5a9505353538b5d43022e4464c" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "user/u", + "value": null + }, + { + "op": "put", + "key": "x/é/a", + "value": true + }, + { + "op": "put", + "key": "x/x", + "value": 0.22385174241472972 + }, + { + "op": "put", + "key": "a/u", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x", + "value": { + "9é\tac￿\"": 4508207094872803, + "9
ybz\u0001": { + "ya 中": 5841994782166.965, + "0 \u0001
y\u0001中": -18446744073709551616, + "9": -2995669663086030 + } + } + }, + { + "op": "delete", + "key": "x/x", + "existed": true + }, + { + "op": "put", + "key": "a/é/x", + "value": [ + [] + ] + }, + { + "op": "put", + "key": "￿/a", + "value": [ + null, + true + ] + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "😀/abc", + "value": -1044675643202406 + }, + { + "op": "commit", + "root": "ed1bf30ac319a09143af82c887700c98328471721f8a544cd4210e58b5ffd7c1" + }, + { + "op": "put", + "key": "😀", + "value": -0.3475467091950055 + }, + { + "op": "put", + "key": "user/abc", + "value": -6568388855102268 + }, + { + "op": "put", + "key": "é/é/abc", + "value": "😀/\\
 中￿
" + }, + { + "op": "delete", + "key": "x/u/users", + "existed": true + }, + { + "op": "put", + "key": "😀/😀", + "value": false + }, + { + "op": "put", + "key": "users", + "value": true + }, + { + "op": "put", + "key": "é/users/￿", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a", + "value": 1152921504606847233 + }, + { + "op": "put", + "key": "a/a", + "value": 456073.1144339554 + }, + { + "op": "put", + "key": "😀", + "value": "/é" + }, + { + "op": "put", + "key": "x", + "value": { + "": { + "￿/中￿￿c😀": "\u0001\\\tz\n\\é\t", + "": null, + "😀yxézc\\": 5366988803890382 + } + } + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": {} + }, + { + "op": "put", + "key": "users/😀", + "value": -0.6007859178460306 + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": { + "": 68284553343612.805 + } + }, + { + "op": "put", + "key": "user/￿/u", + "value": 8235722742674778 + }, + { + "op": "put", + "key": "abc/é", + "value": -6658090824912385 + }, + { + "op": "put", + "key": "￿", + "value": true + }, + { + "op": "put", + "key": "a/é", + "value": 847266905937133 + }, + { + "op": "delete", + "key": "users/ab/é", + "existed": true + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "put", + "key": "users/ab/😀", + "value": [] + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": [ + false + ] + }, + { + "op": "commit", + "root": "106adbd2a72de612ff4465f915a94c34053824dc0375d6cb5bee5cac24dcdc97" + }, + { + "op": "put", + "key": "user", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "a/u", + "existed": true + }, + { + "op": "put", + "key": "users/😀/ab", + "value": false + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "x/￿", + "value": -0.742495377461639 + }, + { + "op": "commit", + "root": "73b93c6687a11ee8c267b72673b89e8c0a8a93e61cf442a10d5764c172a5ce99" + }, + { + "op": "put", + "key": "a/x/😀", + "value": { + "😀/ cz\"￿": { + " 0": "xaa0" + } + } + }, + { + "op": "commit", + "root": "bf2b5b7eb5f8132c7c410d41039c6ae6970e21aed2ba01400498ba4ad2dcb206" + }, + { + "op": "put", + "key": "abc/😀/u", + "value": { + "": "0y", + "￿\\": false, + "9
c\u0001\u00011y": "0￿/" + } + }, + { + "op": "put", + "key": "é/a/a", + "value": [] + }, + { + "op": "put", + "key": "é/é/u", + "value": { + "\u0001\\ 011": null, + "éc￿中ab\\\n": { + "9x\"é\"é\u0001😀": null + }, + "19aa\u0001by": -3212338297784968 + } + }, + { + "op": "put", + "key": "user", + "value": "
" + }, + { + "op": "put", + "key": "user", + "value": { + "": "9\u0001b中\"/x￿" + } + }, + { + "op": "put", + "key": "abc/ab", + "value": { + "￿yab/\n
a": 1152921504606847664, + "中": [ + -142772.81732244696, + "z ab1", + "\\c\"", + null + ] + } + }, + { + "op": "commit", + "root": "1f5988178f117b92e4b7edfeafd954074094ec7f2ba08e98302ebbbe41668461" + }, + { + "op": "commit", + "root": "1f5988178f117b92e4b7edfeafd954074094ec7f2ba08e98302ebbbe41668461" + }, + { + "op": "put", + "key": "users", + "value": {} + }, + { + "op": "put", + "key": "a/users/￿", + "value": [ + { + "y\n": true, + "b😀zé\t": 198487.23636563774, + "￿y\"ya9z": false + }, + "\u0001\t0\n\n91", + 1152921504606847744, + { + "": 0.6799907687918971, + "91\t\t": 6252070170451755, + "1中中z\t \"": -780526.7944130792, + "￿\t￿é\tc": -18446744073709551616 + } + ] + }, + { + "op": "put", + "key": "abc/￿/abc", + "value": -4802163647997278 + }, + { + "op": "delete", + "key": "é/a/a", + "existed": true + }, + { + "op": "put", + "key": "user/é/ab", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "abc/users", + "value": { + "": -0.7713840304213011, + "\\z/cé": { + "\tx￿": null, + "a \u0001yb": null, + "\\\u0001
￿a": 277760.69799884036 + }, + " \\😀￿\u0001": { + "é": false, + "\"\"z": false + }, + "0😀\t": -204132.43975000083 + } + }, + { + "op": "put", + "key": "user/a", + "value": -279452717319622 + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "commit", + "root": "d69e270b4802dad689d81cd0201f90cc9428db0228cfb2565b2c0e4520b584e2" + }, + { + "op": "commit", + "root": "d69e270b4802dad689d81cd0201f90cc9428db0228cfb2565b2c0e4520b584e2" + }, + { + "op": "put", + "key": "a/user", + "value": "￿aa\"x " + }, + { + "op": "put", + "key": "u/u", + "value": { + "\t
": true + } + }, + { + "op": "commit", + "root": "7c41938bf4a87b63f74045fda2d18ed5b6562d4a2524ef020a610d084a22676e" + }, + { + "op": "put", + "key": "ab/ab/users", + "value": [ + 0.8224996794113091, + 714451.1198869103, + 706927.5322352627, + 7602748445201485 + ] + }, + { + "op": "put", + "key": "u/abc", + "value": false + }, + { + "op": "commit", + "root": "8cb12c0bae40eb92694307869f7b9842a0701d8f50910da9c9ef40d0cbfcd56d" + }, + { + "op": "commit", + "root": "8cb12c0bae40eb92694307869f7b9842a0701d8f50910da9c9ef40d0cbfcd56d" + }, + { + "op": "commit", + "root": "8cb12c0bae40eb92694307869f7b9842a0701d8f50910da9c9ef40d0cbfcd56d" + }, + { + "op": "delete", + "key": "users/ab/😀", + "existed": true + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "delete", + "key": "user/a", + "existed": true + }, + { + "op": "delete", + "key": "￿/a", + "existed": true + }, + { + "op": "delete", + "key": "é/é/abc", + "existed": true + }, + { + "op": "commit", + "root": "b59a34cef0c2f9bf6063104a3e5d37794f8167633b01928578d5e364977f7c8c" + }, + { + "op": "delete", + "key": "u/u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": null + }, + { + "op": "delete", + "key": "user/ab/x", + "existed": false + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": false + }, + { + "op": "delete", + "key": "a/users/￿", + "existed": true + }, + { + "op": "delete", + "key": "😀/abc", + "existed": true + }, + { + "op": "delete", + "key": "abc/é", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": 8136182827453579 + }, + { + "op": "put", + "key": "a/ab/x", + "value": [] + }, + { + "op": "put", + "key": "x/user/a", + "value": false + }, + { + "op": "put", + "key": "😀", + "value": { + "/": 1.1641864334751701e-05, + "\"中1/": { + "": true + }, + "z\\": -7127687235979652 + } + }, + { + "op": "delete", + "key": "é/users/￿", + "existed": true + }, + { + "op": "put", + "key": "u/users/a", + "value": {} + }, + { + "op": "put", + "key": "user/user", + "value": "9😀1é" + }, + { + "op": "commit", + "root": "24f4c02943ef913b4af15b0ea95753e2eb2c7d64471498aa3641b938cb647f48" + }, + { + "op": "put", + "key": "😀/é", + "value": "z
\n 😀\t" + }, + { + "op": "put", + "key": "￿/u", + "value": null + }, + { + "op": "put", + "key": "😀/u/￿", + "value": [ + [ + false, + -7095750625121347 + ], + 9.745363582635186, + { + " ": -717185.8436805888 + } + ] + }, + { + "op": "commit", + "root": "75b0da6064b3f7c863bff9d2ab72be7272f222b42f33d06593de99ebf80f9096" + }, + { + "op": "put", + "key": "é", + "value": [ + 8.123583920255413, + 3788.350462644594 + ] + }, + { + "op": "put", + "key": "￿/users", + "value": null + }, + { + "op": "delete", + "key": "user/abc", + "existed": true + }, + { + "op": "commit", + "root": "1123b22bcea29d92b3f228adfa38de37ecccee820af3da1ac51fbc1337c4b1d7" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "abc", + "value": "9é9b/a\n" + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "u/user", + "value": null + }, + { + "op": "put", + "key": "x/user/users", + "value": null + }, + { + "op": "put", + "key": "x/￿/😀", + "value": { + "xac0a": [ + null, + true, + -8681219311921926, + 1152921504606847576 + ], + "9x\\
z": {}, + "0z￿": { + "": false, + "z￿a": false, + "
1 ": -7503386500974739 + }, + "
y😀": 977924.572013817 + } + }, + { + "op": "put", + "key": "a", + "value": " 中 b " + }, + { + "op": "commit", + "root": "51d696a703e47b179b6a15033a9f21f8911fc8176ee532d4b709a0b45c516a2b" + }, + { + "op": "commit", + "root": "51d696a703e47b179b6a15033a9f21f8911fc8176ee532d4b709a0b45c516a2b" + }, + { + "op": "put", + "key": "x", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "d04526e6411d5528d87e1b26e611b1778db3758fc644d07c278042778df92369" + }, + { + "op": "put", + "key": "￿", + "value": 1884570001370224 + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "delete", + "key": "ab/é", + "existed": true + }, + { + "op": "put", + "key": "user/é/a", + "value": true + }, + { + "op": "put", + "key": "x", + "value": "/abax0 " + }, + { + "op": "put", + "key": "a/a/a", + "value": true + }, + { + "op": "put", + "key": "x", + "value": { + "": null, + "x0": 0.4279955369099355, + "z": "￿cz" + } + }, + { + "op": "delete", + "key": "😀/users", + "existed": false + }, + { + "op": "delete", + "key": "abc/😀/u", + "existed": true + }, + { + "op": "delete", + "key": "a/user", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": -831238.3231132485 + }, + { + "op": "put", + "key": "é/a", + "value": 20162852062012.543 + }, + { + "op": "commit", + "root": "584cb3072b8026c481bc13ed4f7b2f320aa26791cb0568906bd5c9759e848f45" + }, + { + "op": "delete", + "key": "a/a/a", + "existed": true + }, + { + "op": "put", + "key": "a/￿/é", + "value": [] + }, + { + "op": "delete", + "key": "users", + "existed": true + }, + { + "op": "put", + "key": "é/a", + "value": 3429741327020023 + }, + { + "op": "put", + "key": "users", + "value": "y\\\n😀by
b" + }, + { + "op": "put", + "key": "ab/￿/user", + "value": 8975440110461416 + }, + { + "op": "put", + "key": "é", + "value": [ + -1480753958253816, + "a
0", + "", + 0.9184893929983005 + ] + }, + { + "op": "put", + "key": "￿/a/é", + "value": true + }, + { + "op": "put", + "key": "user/ab/x", + "value": 816313151259329 + }, + { + "op": "commit", + "root": "f484a40e435afdd52b935ee66137dd9c615655ca910f809f24f659bf6471387e" + }, + { + "op": "put", + "key": "users", + "value": "/￿￿😀
y" + }, + { + "op": "put", + "key": "abc/abc/users", + "value": 4.252731372064616e-12 + }, + { + "op": "put", + "key": "users/x/é", + "value": true + }, + { + "op": "put", + "key": "ab/abc", + "value": { + "0/中a￿a￿": true, + "\n￿\t😀9\n": null + } + }, + { + "op": "put", + "key": "user", + "value": 1152921504606847197 + }, + { + "op": "commit", + "root": "7ca07d72ebdfea07b0a1d068cd3324efa9783d07299437e08a217eacff819867" + }, + { + "op": "put", + "key": "ab/abc", + "value": null + }, + { + "op": "put", + "key": "user/user", + "value": "\u0001é中中" + }, + { + "op": "put", + "key": "x/￿/😀", + "value": { + "\\": false, + "yx/\\\u0001😀c\n": { + "\u0001a中\u0001": "
\n0￿c", + "": false, + "a b\n": 3657686048913665 + }, + "\né9": { + "a9z ": null, + "a0z": -4006062950343854, + "/\\😀": true + } + } + }, + { + "op": "put", + "key": "é", + "value": "90" + }, + { + "op": "commit", + "root": "abcc25e326cb75abcf7e348059dc95a14f990356bcd59a878e79b9d0e51bbc7c" + }, + { + "op": "commit", + "root": "abcc25e326cb75abcf7e348059dc95a14f990356bcd59a878e79b9d0e51bbc7c" + }, + { + "op": "delete", + "key": "user/é/a", + "existed": true + }, + { + "op": "put", + "key": "￿", + "value": { + "9￿1\n\t1
": { + "\u0001\\a": -4500260163213891, + "/0😀
": -0.8120118552736082 + }, + "中1
0": true, + "é\t/é": -5601067476714478 + } + }, + { + "op": "put", + "key": "😀/😀", + "value": [ + "\t\t", + null + ] + }, + { + "op": "commit", + "root": "fe7d422addf21da9771ea36bcedc5e8e87a38f44e0a7c39c3130971f2e6709e3" + }, + { + "op": "delete", + "key": "abc/users", + "existed": true + }, + { + "op": "put", + "key": "😀/user", + "value": "
\nbz\"" + }, + { + "op": "delete", + "key": "x/user/users", + "existed": true + }, + { + "op": "commit", + "root": "89a1a4757156bd56dc6f450446cfdf41fc82509d0d1bf990c02dcd756e20c426" + }, + { + "op": "put", + "key": "é/abc/￿", + "value": 79851996.01838501 + }, + { + "op": "delete", + "key": "é", + "existed": true + }, + { + "op": "put", + "key": "😀/users/￿", + "value": -0.8636252350646845 + }, + { + "op": "put", + "key": "é/abc", + "value": 7981756839298357 + }, + { + "op": "delete", + "key": "é/abc", + "existed": true + }, + { + "op": "delete", + "key": "user/user", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": 1152921504606847204 + }, + { + "op": "put", + "key": "é", + "value": false + }, + { + "op": "put", + "key": "é", + "value": 0.00029552700270127027 + }, + { + "op": "put", + "key": "a", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "😀", + "value": [ + [ + null, + null, + "y/中1111\n", + -18446744073709551616 + ], + -3422922777074208 + ] + }, + { + "op": "delete", + "key": "￿/u", + "existed": true + }, + { + "op": "delete", + "key": "￿/x", + "existed": false + }, + { + "op": "put", + "key": "￿", + "value": { + "b\n\u0001\\c": true, + "bz1\"\\/￿": -18446744073709551616, + "\t😀\n\nyé￿y": 811883.0582577325, + "c\\中\tz/": { + "": null + } + } + }, + { + "op": "put", + "key": "￿", + "value": "xx" + }, + { + "op": "put", + "key": "u", + "value": 1152921504606847016 + }, + { + "op": "put", + "key": "￿/user/abc", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": 975825.6783753687 + }, + { + "op": "put", + "key": "😀/￿/users", + "value": 1298350660019815 + }, + { + "op": "put", + "key": "x/users", + "value": {} + }, + { + "op": "put", + "key": "😀/u/a", + "value": {} + }, + { + "op": "commit", + "root": "c6349ca1babc082e1ce8de16011dd0fa13d1e872699366b73fcbfbb494bbbe8f" + }, + { + "op": "delete", + "key": "ab/abc", + "existed": true + }, + { + "op": "put", + "key": "x/😀", + "value": { + "y￿￿ ": { + "x": null, + "x/b a0": "\"
" + }, + "
yc9￿": false, + "😀1": null + } + }, + { + "op": "commit", + "root": "c825ad5c2a49c6eaf7007128d097a7b301945fe1c318b72a3db5bc8c1830aba2" + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "ab", + "value": { + "😀b": -199200.11279452336, + "/c 0cc": -18446744073709551616, + "ay
\"\" \u0001é": null + } + }, + { + "op": "put", + "key": "￿/x", + "value": -4457290127836679 + }, + { + "op": "commit", + "root": "3c59bd869ffaab129ca04b84afef708305a596533c72db4142dc7bbbd292ae85" + }, + { + "op": "put", + "key": "user/é", + "value": {} + }, + { + "op": "commit", + "root": "fd32d1c9a1d1d7a6e45e3186aeff3ff29306f5123d096729ce62a5647fd1013d" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "commit", + "root": "9ed7bbbbe5778e14e41c2029408c3792517ba848574b752f4ba2fb433b87053c" + }, + { + "op": "put", + "key": "é/ab", + "value": true + }, + { + "op": "delete", + "key": "😀/😀", + "existed": true + }, + { + "op": "delete", + "key": "😀/￿/users", + "existed": true + }, + { + "op": "delete", + "key": "￿/é", + "existed": false + }, + { + "op": "delete", + "key": "😀/u/a", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": false + }, + { + "op": "delete", + "key": "u/ab/abc", + "existed": true + }, + { + "op": "delete", + "key": "😀/u/￿", + "existed": true + }, + { + "op": "delete", + "key": "￿", + "existed": true + }, + { + "op": "delete", + "key": "x/￿/😀", + "existed": true + }, + { + "op": "delete", + "key": "😀/users/￿", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": [ + 683501.1972635924, + {}, + 0.19109490539973928 + ] + }, + { + "op": "put", + "key": "users/abc/x", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "x", + "value": [ + {} + ] + }, + { + "op": "delete", + "key": "￿/x", + "existed": true + }, + { + "op": "commit", + "root": "bfc7ea0697759fa7ce8dd7f88b41788ce610ea578b7115726f99e32a932288f2" + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "put", + "key": "x/abc", + "value": "中中" + }, + { + "op": "put", + "key": "abc", + "value": [ + { + "zcy ": "\"中\u0001c￿9", + " x中😀9 x": "￿x￿\n

ba", + "
0/0\t中": 6430599794759789, + "/\"\\/a中é": -18446744073709551616 + }, + 0.6293664603917879, + 58024990733594.93, + { + "/a\\\n
 ": null + } + ] + }, + { + "op": "put", + "key": "ab/ab/users", + "value": null + }, + { + "op": "commit", + "root": "113f2e24df95fdd1764a1e7cc94f838088dcf573bcf71e37b7d179cd2d50f3e1" + }, + { + "op": "put", + "key": "é/é", + "value": 0.9885952857249212 + }, + { + "op": "put", + "key": "a", + "value": { + "中bxc\n \n/": "0
中\nc
x0", + "": {}, + "￿z￿😀c\"": { + "￿😀\\9a0a": 0.10172587710287129, + "
\n\"z0 \\": 27745454571002.375, + "中y\u0001中é": false, + "\"": "" + } + } + }, + { + "op": "commit", + "root": "52fef9d76c7ffd420b4f1a29d4f3dfa83a557c3b0fa689db3f9ee4a4fa7277e6" + }, + { + "op": "commit", + "root": "52fef9d76c7ffd420b4f1a29d4f3dfa83a557c3b0fa689db3f9ee4a4fa7277e6" + }, + { + "op": "put", + "key": "😀", + "value": "xx😀/a\u0001😀" + }, + { + "op": "put", + "key": "a", + "value": "z0\\9中😀￿" + }, + { + "op": "put", + "key": "ab/é", + "value": "\"90
😀\\z" + }, + { + "op": "put", + "key": "😀", + "value": { + "
中\u0001a0": "" + } + }, + { + "op": "commit", + "root": "1f91b71c99f2be8f8286a6c37afadb4e42038dc13b0396d120032d3d27a18381" + }, + { + "op": "put", + "key": "user", + "value": true + }, + { + "op": "put", + "key": "users", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "a/é/ab", + "value": -0.8878078883743652 + }, + { + "op": "put", + "key": "user", + "value": { + "z\\\\1": 617485.7139944278, + "": false, + "z
1y\"
b": 1152921504606847838 + } + }, + { + "op": "put", + "key": "a", + "value": "a9" + }, + { + "op": "put", + "key": "😀/user/é", + "value": -8402171115934331 + }, + { + "op": "put", + "key": "abc", + "value": 5583860529011803 + }, + { + "op": "commit", + "root": "2f8bc65ad9bb12a2ffd1b20c4847ecfabf0117a147cf7309841bf562581def58" + }, + { + "op": "put", + "key": "user/￿", + "value": [] + }, + { + "op": "delete", + "key": "￿/user/abc", + "existed": true + }, + { + "op": "commit", + "root": "c9bf2aaa5d36afe7157cd4a1c5f547a73cba82279ba5d0c18693ee110823bc84" + }, + { + "op": "commit", + "root": "c9bf2aaa5d36afe7157cd4a1c5f547a73cba82279ba5d0c18693ee110823bc84" + }, + { + "op": "put", + "key": "x/u", + "value": 3745147572404717 + }, + { + "op": "put", + "key": "a", + "value": { + "0\\": [ + null, + -206589143784449 + ], + "a\n ": "" + } + }, + { + "op": "delete", + "key": "u/abc", + "existed": true + }, + { + "op": "delete", + "key": "a/x/😀", + "existed": true + }, + { + "op": "commit", + "root": "c41a913bf40207cc9edd3af6bc2d219208744d8b7a2eeeb48bc089bd77743f3b" + }, + { + "op": "delete", + "key": "user/￿/u", + "existed": true + }, + { + "op": "commit", + "root": "3aa0dd79a9b880f07f67148f623811a10b1189284357568156625945da1accad" + }, + { + "op": "delete", + "key": "u/ab", + "existed": false + }, + { + "op": "put", + "key": "abc", + "value": "yc😀" + }, + { + "op": "delete", + "key": "user", + "existed": true + }, + { + "op": "delete", + "key": "users/x/é", + "existed": true + }, + { + "op": "commit", + "root": "55af260959ecbcf2c8287029aece71721a9e031640271e320f13bebd06ee80f3" + }, + { + "op": "put", + "key": "é", + "value": 1152921504606847920 + }, + { + "op": "delete", + "key": "users/😀", + "existed": true + }, + { + "op": "put", + "key": "￿/😀", + "value": -0.3082166045842658 + }, + { + "op": "put", + "key": "x/ab", + "value": [ + -18446744073709551616 + ] + }, + { + "op": "put", + "key": "x/user/user", + "value": false + }, + { + "op": "commit", + "root": "592bc070abcd7c5d3da2be0d4b9360059eba31edcb5f3ecde65e9e87a94b3ec4" + }, + { + "op": "put", + "key": "😀", + "value": "中\"0😀bz/中" + }, + { + "op": "put", + "key": "abc/x", + "value": { + " \n\"": 92.14966227236646 + } + }, + { + "op": "commit", + "root": "af889f82cdb37d2ba4b662aacb4935c02d37979b310e17cb66a8f69f0a126ff0" + }, + { + "op": "put", + "key": "x/abc", + "value": "中😀\"a\t" + }, + { + "op": "delete", + "key": "😀/user", + "existed": true + }, + { + "op": "delete", + "key": "abc/abc/users", + "existed": true + }, + { + "op": "delete", + "key": "a", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": -18446744073709551616 + }, + { + "op": "commit", + "root": "2fe6c4f24d9e32306fba454f2413b0eac462e17df7d56fdb5fbdbcb627a93e9a" + }, + { + "op": "delete", + "key": "a/ab/x", + "existed": true + }, + { + "op": "put", + "key": "user", + "value": 426159.6239946245 + }, + { + "op": "commit", + "root": "4bb32d774848ece97cb7213c4fe0260b2f4be1ced8b49ff889736eca3f7903fb" + }, + { + "op": "delete", + "key": "a/a", + "existed": true + }, + { + "op": "delete", + "key": "a/￿/é", + "existed": true + }, + { + "op": "delete", + "key": "users/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "x/a", + "existed": false + }, + { + "op": "delete", + "key": "é/ab", + "existed": true + }, + { + "op": "put", + "key": "é/￿", + "value": 0.07561877714596606 + }, + { + "op": "put", + "key": "ab/a", + "value": { + "\ncc": true, + "\\\n\tza": { + "\nc\n中z 1": 1152921504606847697 + } + } + }, + { + "op": "delete", + "key": "u/users/a", + "existed": true + }, + { + "op": "put", + "key": "x/abc", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "u", + "value": {} + }, + { + "op": "put", + "key": "😀/user", + "value": -3472000912914411 + }, + { + "op": "put", + "key": "a/a", + "value": 0.06132866827788308 + }, + { + "op": "put", + "key": "a/user/users", + "value": -0.9172011080678681 + }, + { + "op": "delete", + "key": "😀/user", + "existed": true + }, + { + "op": "put", + "key": "￿/￿/abc", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "x", + "existed": true + }, + { + "op": "put", + "key": "abc/user", + "value": {} + }, + { + "op": "commit", + "root": "4f6432d85250fdf263d9af81add321ebb14a54d26a45355dfd8b7c09a6415fce" + }, + { + "op": "delete", + "key": "user/￿", + "existed": true + }, + { + "op": "commit", + "root": "3e84385523803958e33096cb156795165659052d7a9e34ab5bfd23d3c47efb13" + }, + { + "op": "put", + "key": "u/user/abc", + "value": 3543116664759.4956 + }, + { + "op": "delete", + "key": "abc/￿/abc", + "existed": true + }, + { + "op": "delete", + "key": "user/é/ab", + "existed": true + }, + { + "op": "put", + "key": "u/a/abc", + "value": [ + false, + "c😀
x" + ] + }, + { + "op": "put", + "key": "u", + "value": { + "
x\"\u0001￿é9": false + } + }, + { + "op": "commit", + "root": "c6215918180b5fd6d91fb4399227042d70dd7b2d4f55e9e4a3bdf9fd8c7aae64" + }, + { + "op": "delete", + "key": "abc/user", + "existed": true + }, + { + "op": "put", + "key": "u/é", + "value": {} + }, + { + "op": "put", + "key": "a/users", + "value": null + }, + { + "op": "put", + "key": "￿", + "value": -18446744073709551616 + }, + { + "op": "put", + "key": "users/x", + "value": [ + -18446744073709551616, + { + "\na1c": 1892193662427921, + "": -471541699076132, + "\u0001
\u0001x\"\"": "1\"xy
\\￿\"" + }, + {}, + [ + null, + true + ] + ] + }, + { + "op": "delete", + "key": "é/é/u", + "existed": true + }, + { + "op": "put", + "key": "u", + "value": 0.0699746458055941 + }, + { + "op": "put", + "key": "a/é", + "value": false + }, + { + "op": "delete", + "key": "x/user/user", + "existed": true + }, + { + "op": "delete", + "key": "é/a", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": { + "": null + } + }, + { + "op": "put", + "key": "😀", + "value": { + "a1bc
\u0001": null, + "￿\u000191": {}, + "1中￿": true, + "": [ + "x中9￿", + "", + null, + -7682083297527074 + ] + } + }, + { + "op": "put", + "key": "ab/a/user", + "value": { + "": { + "1": -6778640052511576, + " yzz": null, + "y": 191967.5581819946, + "\n中0": true + }, + "\" c\\x": -7215896155523050, + "\\\u0001\t c": 1152921504606847341 + } + }, + { + "op": "commit", + "root": "e0f7d29f83e627e782799b7639e430ad4d5a33210d88a3f4dcfb42395c52f7e8" + }, + { + "op": "delete", + "key": "x/é/a", + "existed": true + }, + { + "op": "put", + "key": "x/é", + "value": false + }, + { + "op": "delete", + "key": "abc", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "ab/a", + "existed": true + }, + { + "op": "put", + "key": "x/users", + "value": 1152921504606847806 + }, + { + "op": "commit", + "root": "3c8e4e2b0dd5f8cbc7eaaa23957ac8dd7084eda5abd9d693dfb8a45ec683129c" + }, + { + "op": "put", + "key": "users", + "value": { + "é": "\n ￿\"中/", + "\u0001\n\n\t": true, + "
": [], + "
b\tzy\"0c": true + } + }, + { + "op": "put", + "key": "￿", + "value": 26437716273043 + }, + { + "op": "delete", + "key": "x/abc", + "existed": true + }, + { + "op": "put", + "key": "a/😀/x", + "value": {} + }, + { + "op": "put", + "key": "😀", + "value": {} + }, + { + "op": "put", + "key": "u/abc/￿", + "value": { + " 
cy": null, + "c\n": -944359.3880934673 + } + }, + { + "op": "delete", + "key": "ab/é", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": { + "/": true, + "éz9y ": null + } + }, + { + "op": "commit", + "root": "7b0b2ed327216e9aa9d6d085f42844a49d63f151f95e82c888ea7336a11f12d8" + }, + { + "op": "commit", + "root": "7b0b2ed327216e9aa9d6d085f42844a49d63f151f95e82c888ea7336a11f12d8" + }, + { + "op": "put", + "key": "😀", + "value": false + }, + { + "op": "put", + "key": "😀/ab", + "value": 5798900.91171178 + }, + { + "op": "put", + "key": "a/u", + "value": false + }, + { + "op": "commit", + "root": "1ba7e3ce1486e4a595999a02ebac3eabff9c7e9bdf13ba93e634468e3cc6cd3b" + }, + { + "op": "commit", + "root": "1ba7e3ce1486e4a595999a02ebac3eabff9c7e9bdf13ba93e634468e3cc6cd3b" + }, + { + "op": "put", + "key": "a", + "value": [ + { + "": true, + "/😀": -5655752702209879, + "\u0001\u0001\"\"😀
": -460660283449905 + }, + null, + "a
90b
a
" + ] + }, + { + "op": "put", + "key": "x", + "value": -532135.6385488648 + }, + { + "op": "delete", + "key": "😀/user/é", + "existed": true + }, + { + "op": "put", + "key": "users/x/a", + "value": {} + }, + { + "op": "delete", + "key": "x/user/a", + "existed": true + }, + { + "op": "put", + "key": "users", + "value": { + "/y\u0001\"": "9x￿z/é\"
" + } + }, + { + "op": "delete", + "key": "é/￿", + "existed": true + }, + { + "op": "delete", + "key": "u", + "existed": true + }, + { + "op": "commit", + "root": "c41adbec64523fe3f106c910ffbf15fc9cce842559c09f4d30ff3fff1b58bb95" + }, + { + "op": "put", + "key": "ab", + "value": { + "0": true, + "11": "" + } + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "delete", + "key": "￿/user/ab", + "existed": false + }, + { + "op": "put", + "key": "😀/x", + "value": {} + }, + { + "op": "put", + "key": "😀", + "value": [] + }, + { + "op": "put", + "key": "x/￿/ab", + "value": [] + }, + { + "op": "put", + "key": "u", + "value": { + "\t9中c y": { + "1中z￿😀a1": 2.237744424848274, + "9
中/": -2847243301517036, + "\t": 1554006749238746 + }, + "\tcc￿": 3166238056711594 + } + }, + { + "op": "put", + "key": "abc", + "value": -336747.62071530614 + }, + { + "op": "put", + "key": "users/é/😀", + "value": [ + -18446744073709551616, + {} + ] + }, + { + "op": "delete", + "key": "😀/x", + "existed": true + }, + { + "op": "delete", + "key": "￿/￿/abc", + "existed": true + }, + { + "op": "delete", + "key": "é/😀/ab", + "existed": true + }, + { + "op": "delete", + "key": "😀/é", + "existed": true + }, + { + "op": "delete", + "key": "u/é", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": -6224794831260817 + }, + { + "op": "put", + "key": "😀/￿", + "value": -18446744073709551616 + }, + { + "op": "delete", + "key": "user/ab/x", + "existed": true + }, + { + "op": "delete", + "key": "😀", + "existed": true + }, + { + "op": "put", + "key": "a", + "value": "\\\tbc￿" + }, + { + "op": "put", + "key": "😀/😀/ab", + "value": { + "zy": -4365457848758776, + "
": "/0\t99", + "0\\9b😀zx": { + "\\": -251140734133170, + "￿": true, + "\u0001écb90￿😀": null, + "": null + }, + "": { + "😀c \n/b\\": -18446744073709551616, + "1x
xb
\u00011": -18446744073709551616, + "￿\"\t 9\\": "\t" + } + } + }, + { + "op": "put", + "key": "😀", + "value": [ + null, + { + "cc\t😀\"bx0": null + } + ] + }, + { + "op": "put", + "key": "users/😀", + "value": -1461530824469002 + }, + { + "op": "delete", + "key": "u/a/abc", + "existed": true + }, + { + "op": "delete", + "key": "ab/￿/user", + "existed": true + }, + { + "op": "put", + "key": "é", + "value": 1152921504606847348 + }, + { + "op": "delete", + "key": "x/u", + "existed": true + }, + { + "op": "put", + "key": "é/users", + "value": { + "": null, + "0x/
\"z\n": "b0\t" + } + }, + { + "op": "put", + "key": "😀", + "value": [ + -18446744073709551616, + false, + true + ] + }, + { + "op": "put", + "key": "a", + "value": [ + 582857.2323208617, + 1020079857923796, + 1152921504606847959 + ] + }, + { + "op": "put", + "key": "😀", + "value": 1152921504606847269 + }, + { + "op": "commit", + "root": "688722546d599e1a6925095422d375baa4d133e5f6670cf75969f7215971d3fe" + }, + { + "op": "put", + "key": "é", + "value": 7.442987188463953 + }, + { + "op": "commit", + "root": "2ab87f68d034916fa3d96b5b1bcd7594aa0b51fed3ca6367fad21a61720a61be" + } + ], + "items": [ + [ + "a", + [ + 582857.2323208617, + 1020079857923796, + 1152921504606847959 + ] + ], + [ + "a/a", + 0.06132866827788308 + ], + [ + "a/u", + false + ], + [ + "a/user/users", + -0.9172011080678681 + ], + [ + "a/users", + null + ], + [ + "a/é", + false + ], + [ + "a/é/ab", + -0.8878078883743652 + ], + [ + "a/é/x", + [ + [] + ] + ], + [ + "a/😀/x", + {} + ], + [ + "ab", + { + "0": true, + "11": "" + } + ], + [ + "ab/a/user", + { + "": { + "\n中0": true, + " yzz": null, + "1": -6778640052511576, + "y": 191967.5581819946 + }, + "\" c\\x": -7215896155523050, + "\\\u0001\t c": 1152921504606847341 + } + ], + [ + "ab/ab/users", + null + ], + [ + "abc", + -336747.62071530614 + ], + [ + "abc/ab", + { + "中": [ + -142772.81732244696, + "z ab1", + "\\c\"", + null + ], + "￿yab/\n
a": 1152921504606847664 + } + ], + [ + "abc/x", + { + " \n\"": 92.14966227236646 + } + ], + [ + "u", + { + "\t9中c y": { + "\t": 1554006749238746, + "1中z￿😀a1": 2.237744424848274, + "9
中/": -2847243301517036 + }, + "\tcc￿": 3166238056711594 + } + ], + [ + "u/abc/￿", + { + " 
cy": null, + "c\n": -944359.3880934673 + } + ], + [ + "u/user", + null + ], + [ + "u/user/abc", + 3543116664759.4956 + ], + [ + "u/users", + { + "😀\\\u0001é/y/9": "
a中yb9" + } + ], + [ + "user", + 426159.6239946245 + ], + [ + "user/u", + null + ], + [ + "user/é", + {} + ], + [ + "users", + { + "/y\u0001\"": "9x￿z/é\"
" + } + ], + [ + "users/abc/x", + -18446744073709551616 + ], + [ + "users/x", + [ + -18446744073709551616, + { + "": -471541699076132, + "\u0001
\u0001x\"\"": "1\"xy
\\￿\"", + "\na1c": 1892193662427921 + }, + {}, + [ + null, + true + ] + ] + ], + [ + "users/x/a", + {} + ], + [ + "users/é/😀", + [ + -18446744073709551616, + {} + ] + ], + [ + "users/😀", + -1461530824469002 + ], + [ + "x", + -532135.6385488648 + ], + [ + "x/ab", + [ + -18446744073709551616 + ] + ], + [ + "x/users", + 1152921504606847806 + ], + [ + "x/é", + false + ], + [ + "x/￿", + -0.742495377461639 + ], + [ + "x/￿/ab", + [] + ], + [ + "x/😀", + { + "y￿￿ ": { + "x": null, + "x/b a0": "\"
" + }, + "
yc9￿": false, + "😀1": null + } + ], + [ + "é", + 7.442987188463953 + ], + [ + "é/abc/￿", + 79851996.01838501 + ], + [ + "é/users", + { + "": null, + "0x/
\"z\n": "b0\t" + } + ], + [ + "é/é", + 0.9885952857249212 + ], + [ + "￿", + 26437716273043 + ], + [ + "￿/a/é", + true + ], + [ + "￿/users", + null + ], + [ + "￿/😀", + -0.3082166045842658 + ], + [ + "😀", + 1152921504606847269 + ], + [ + "😀/ab", + 5798900.91171178 + ], + [ + "😀/￿", + -18446744073709551616 + ], + [ + "😀/😀/ab", + { + "": { + "1x
xb
\u00011": -18446744073709551616, + "￿\"\t 9\\": "\t", + "😀c \n/b\\": -18446744073709551616 + }, + "0\\9b😀zx": { + "": null, + "\u0001écb90￿😀": null, + "\\": -251140734133170, + "￿": true + }, + "zy": -4365457848758776, + "
": "/0\t99" + } + ] + ] + } + ] +} diff --git a/js/test/fixtures/swarm-addresses.json b/js/test/fixtures/swarm-addresses.json new file mode 100644 index 0000000..3e74f2b --- /dev/null +++ b/js/test/fixtures/swarm-addresses.json @@ -0,0 +1,66 @@ +{ + "note": "Generated by js/tools/make_fixtures.py from the Python reference implementation; do not edit by hand.", + "data": "byte i = (i * 31 + 7) & 0xff", + "cases": [ + { + "size": 0, + "ref": "b34ca8c22b9e982354f9c7f50b470d66db428d880c8a904d5fe4ec9713171526" + }, + { + "size": 1, + "ref": "b073f3782b5a9db35d5642aacb734064fc421fd277a882b6fd177c744d0ad10d" + }, + { + "size": 31, + "ref": "3026a29b50c0254cab38b3800171f8968e524610c5f1aa69464c49a0b8377182" + }, + { + "size": 32, + "ref": "cfd2d9919025db2e91299b9d0f2e1c253665363c3f3b5acbd8329b7db5db96b1" + }, + { + "size": 33, + "ref": "fb8c581d61795b75d51208020f9c8261f8149aa729d005e54301710cd694b191" + }, + { + "size": 64, + "ref": "f90150e6470e6a22b0ee50d40f87736ba9e38f966b187b67dc137bdbaa9bc121" + }, + { + "size": 4095, + "ref": "f137b04ca81dc566928fcec4f5e37c10a0c27b30ddb1d535ea4c2d4aebdcaa03" + }, + { + "size": 4096, + "ref": "ee1ccdcda5351ae064dcaf614b583e2e7a8a42ca4df5f5f5eb3b7559ac24b0ec" + }, + { + "size": 4097, + "ref": "bd3357b006abe623abfef563dd07f5cd9d496e301f404ad3d7767e20abc24ef6" + }, + { + "size": 8192, + "ref": "ed190bdd51f9ca81eefd1817a605caf3136bf9f24d32e52e97cb3f521ad7434b" + }, + { + "size": 8193, + "ref": "0849b8d414cb0204e005368bf4feb95a8da39ee4d5e7e400e251d1520e68680e" + }, + { + "size": 524288, + "ref": "e3a273803ee6200d328d15f8a838eb5cc9124e4bb15114b517ae0a7b406cbcdf" + }, + { + "size": 524289, + "ref": "d02da6e441f6fc652c6724357fe23faff6c454a6dae152f3c7c88d894be98fe1" + }, + { + "size": 528384, + "ref": "21165bba025f93fb98a2f1feef21396960af463d5c48c70187758ec98828898a" + }, + { + "size": 1048581, + "ref": "8cc2eab297e99267865d7c3c273531acc6296b8ce715f7dec5b4fdfe0a9dc772" + } + ] +} diff --git a/js/test/freedom/freedom_check.cjs b/js/test/freedom/freedom_check.cjs new file mode 100644 index 0000000..ecd2862 --- /dev/null +++ b/js/test/freedom/freedom_check.cjs @@ -0,0 +1,156 @@ +// Runs test/freedom/provider-check.html in real Freedom Browser, from a +// freedom-browser checkout, and prints what it found as JSON. +// +// node test/freedom/freedom_check.cjs [FREEDOM_DIR] [--bee=URL] [--write] +// +// FREEDOM_DIR: a freedom-browser checkout with its node_modules (default: +// beside this repository). Freedom is launched the way its live tests and +// ontodag's check launch it: a throwaway profile, and either its own Ant +// node (default) or, with --bee=URL, that Bee node as its external Swarm +// node. The page is served from this package's directory on 127.0.0.1 and +// opened through the address bar, so it reads and writes through Freedom's +// real window.swarm provider. +// +// Without --write only the read check runs. With --write the page also +// writes a small store, which spends stamps: the check approves Freedom's +// connect prompt and its publish prompt (ticking "Always allow this site", +// so the commit's chunks need one approval). Freedom's node needs a usable +// postage batch for that; without one the write fails with no-usable-stamps. +// +// Report and screenshots go to a temporary directory, named in the output. + +const fs = require('fs'); +const http = require('http'); +const os = require('os'); +const path = require('path'); +const { createRequire } = require('module'); + +const args = process.argv.slice(2); +const positional = args.filter((a) => !a.startsWith('--')); +const flag = (name) => args.find((a) => a === `--${name}` || a.startsWith(`--${name}=`)); +const bee = (flag('bee') || '').split('=')[1] || null; +const doWrite = Boolean(flag('write')); +const PACKAGE = path.resolve(__dirname, '..', '..'); +const freedomDir = path.resolve(positional[0] || path.join(PACKAGE, '..', '..', 'freedom-browser')); +const { _electron: electron } = createRequire(path.join(freedomDir, 'package.json'))('@playwright/test'); + +const TYPES = { '.html': 'text/html', '.js': 'text/javascript', '.json': 'application/json', '.css': 'text/css' }; + +/** Serve this package's directory (read-only, no listing) on 127.0.0.1. */ +function serve() { + const server = http.createServer((req, res) => { + const rel = decodeURIComponent(new URL(req.url, 'http://x').pathname).replace(/^\/+/, ''); + const file = path.resolve(PACKAGE, rel); + if (!file.startsWith(PACKAGE + path.sep) || !fs.existsSync(file) || fs.statSync(file).isDirectory()) { + res.writeHead(404).end(); + return; + } + res.writeHead(200, { 'content-type': TYPES[path.extname(file)] || 'application/octet-stream', 'cache-control': 'no-store' }); + fs.createReadStream(file).pipe(res); + }); + return new Promise((resolve) => server.listen(0, '127.0.0.1', () => resolve(server))); +} + +function inPage(window, code) { + return window.evaluate(async (snippet) => { + const wv = document.querySelector('webview:not(.hidden)'); + if (!wv || typeof wv.executeJavaScript !== 'function') return { error: 'no webview' }; + try { return await wv.executeJavaScript(snippet); } catch (e) { return { error: String(e) }; } + }, code); +} + +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + +async function visible(window, selector) { + return window.locator(selector).isVisible().catch(() => false); +} + +async function main() { + const server = await serve(); + const url = `http://127.0.0.1:${server.address().port}/test/freedom/provider-check.html`; + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'recordstore-freedom-')); + const dirs = Object.fromEntries(['userData', 'ant-data', 'ipfs-data', 'identity'].map((d) => { + const p = path.join(tmp, d); + fs.mkdirSync(p, { recursive: true }); + return [d, p]; + })); + fs.writeFileSync(path.join(dirs.userData, 'settings.json'), JSON.stringify({ + startIpfsAtLaunch: false, startRadicleAtLaunch: false, startTorAtLaunch: false, + })); + fs.writeFileSync(path.join(dirs.userData, 'external-node-decisions.json'), JSON.stringify({ + bee: bee ? { choice: 'external', endpoints: [bee] } : { choice: 'managed', endpoints: ['http://127.0.0.1:1633'] }, + })); + const app = await electron.launch({ + args: ['.'], cwd: freedomDir, timeout: 90_000, + env: { + ...process.env, + FREEDOM_TEST_USER_DATA: dirs.userData, FREEDOM_ANT_DATA: dirs['ant-data'], + FREEDOM_IPFS_DATA: dirs['ipfs-data'], FREEDOM_IDENTITY_DATA: dirs.identity, + ELECTRON_DISABLE_SECURITY_WARNINGS: 'true', LANG: 'en_US.UTF-8', + }, + }); + const out = { url, node: bee ? `external ${bee}` : "Freedom's own Ant", profile: tmp }; + try { + const window = await app.firstWindow(); + await window.waitForLoadState('domcontentloaded'); + const input = window.locator('[data-test="address-input"]'); + await input.waitFor({ state: 'visible', timeout: 60_000 }); + await input.click(); + await input.fill(url); + await input.press('Enter'); + let started = Date.now(); + while (Date.now() - started < 60_000) { + if ((await inPage(window, 'Boolean(window.rsCheck && window.rsCheck.ready)')) === true) break; + await sleep(1000); + } + out.provider = await inPage(window, 'window.rsCheck ? window.rsCheck.provider : null'); + + // Reads may wait for the node to find peers: retry for a few minutes. + started = Date.now(); + for (;;) { + out.read = await inPage(window, 'window.rsCheck.read()'); + if (!out.read || !out.read.error || Date.now() - started > 300_000) break; + out.readRetries = (out.readRetries || 0) + 1; + await sleep(15_000); + } + out.feed = await inPage(window, 'window.rsCheck.feed()'); + await window.screenshot({ path: path.join(tmp, 'read.png') }); + + if (doWrite) { + await inPage(window, 'window.rsCheck.pendingWrite = window.rsCheck.write(), true'); + const prompts = []; + started = Date.now(); + while (Date.now() - started < 600_000) { + if (await visible(window, '#swarm-connect-approve')) { + await window.waitForFunction(() => !document.getElementById('swarm-connect-approve').disabled, null, { timeout: 30_000 }); + await window.locator('#swarm-connect-approve').click(); + prompts.push('connect approved'); + } else if (await visible(window, '#swarm-publish-confirm')) { + const always = window.locator('#swarm-publish-auto-approve'); + if (!(await always.isChecked().catch(() => false))) await always.check({ force: true }).catch(() => {}); + const ticked = await always.isChecked().catch(() => false); + await window.locator('#swarm-publish-confirm').click(); + prompts.push(`publish approved (always allow ${ticked ? 'ticked' : 'NOT ticked'})`); + } + const result = await inPage(window, 'window.rsCheck.results.write || null'); + if (result) { + out.write = result; + break; + } + await sleep(1000); + } + out.prompts = prompts; + await window.screenshot({ path: path.join(tmp, 'write.png') }); + } + } finally { + await app.close().catch(() => {}); + server.close(); + } + fs.writeFileSync(path.join(tmp, 'report.json'), JSON.stringify(out, null, 1)); + console.log(JSON.stringify(out, null, 1)); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/js/test/freedom/provider-check.html b/js/test/freedom/provider-check.html new file mode 100644 index 0000000..b17a057 --- /dev/null +++ b/js/test/freedom/provider-check.html @@ -0,0 +1,26 @@ + + + + + +recordstore: window.swarm check + + + +

recordstore through window.swarm

+

Reads a store Python published, then writes a small store through Freedom's +provider and checks its root against the one computed offline. +Run it in Freedom Browser; test/freedom/freedom_check.cjs drives it.

+

+ + +

+

loading…

+

+
+
+
diff --git a/js/test/freedom/provider-check.js b/js/test/freedom/provider-check.js
new file mode 100644
index 0000000..6277970
--- /dev/null
+++ b/js/test/freedom/provider-check.js
@@ -0,0 +1,130 @@
+// Checks recordstore's SwarmProviderBytesStore inside Freedom Browser.
+//
+// window.rsCheck.read()  reads OntoDAG's `space` pack (written by Python) through
+//                        window.swarm: a few records and the keys under a prefix.
+// window.rsCheck.write() writes a small store through window.swarm (one record
+//                        over 4096 bytes, so a chunk tree with an intermediate),
+//                        checks that its root equals the root computed offline
+//                        under Swarm addressing, and reads it back.
+// Both resolve to a plain object (also kept in window.rsCheck.results) and
+// never throw: an error comes back as {error, reason}.
+
+import {
+  MemoryBytesStore, ProviderFeedTransport, RecordStore, SwarmFeedPointer, SwarmProviderBytesStore, canonicalJson,
+} from '../../src/index.js';
+
+const SPACE_PACK = '67f6be2688e5fb7ca85a463c3d2929dad2b177bb3edb6bb910e8fa110598cff3';
+const $ = (id) => document.getElementById(id);
+
+function show(name, result) {
+  window.rsCheck.results[name] = result;
+  $('out').textContent = JSON.stringify(window.rsCheck.results, null, 2);
+  $('status').textContent = `${name}: ${result.error ? `failed (${result.reason || result.error})` : 'done'}`;
+  return result;
+}
+
+function failure(e) {
+  return { error: String(e && e.message || e), reason: e && e.data && e.data.reason, code: e && e.code };
+}
+
+async function collect(iterable) {
+  const out = [];
+  for await (const x of iterable) out.push(x);
+  return out;
+}
+
+/** A provider wrapper that counts calls per method. */
+function counting(provider) {
+  const calls = {};
+  return {
+    calls,
+    request(args) {
+      calls[args.method] = (calls[args.method] || 0) + 1;
+      return provider.request(args);
+    },
+  };
+}
+
+async function read(root = SPACE_PACK, prefix = 'ab', keys = ['abbey', 'abalone', 'not-a-key']) {
+  $('status').textContent = 'reading…';
+  const provider = counting(window.swarm);
+  const started = performance.now();
+  try {
+    const store = RecordStore.at(root, new SwarmProviderBytesStore(provider));
+    const values = {};
+    for (const k of keys) values[k] = await store.get(k);
+    const under = await collect(store.keys(prefix));
+    return show('read', {
+      root, values, prefix, keysUnderPrefix: under.length, firstKeys: under.slice(0, 5),
+      ms: Math.round(performance.now() - started), calls: provider.calls,
+    });
+  } catch (e) {
+    return show('read', { ...failure(e), ms: Math.round(performance.now() - started), calls: provider.calls });
+  }
+}
+
+/** Follow a store Python keeps behind a feed (loopmarket's test maker book), all through window.swarm. */
+async function feed(topic = 'loop-twonode-1791730596', owner = '300f4daa2653102abde4c66f75ea512c0bb81571') {
+  $('status').textContent = 'following a feed…';
+  const provider = counting(window.swarm);
+  const started = performance.now();
+  try {
+    const pointer = new SwarmFeedPointer(new ProviderFeedTransport(provider), topic, { owner });
+    const store = await RecordStore.open(new SwarmProviderBytesStore(provider), { pointer });
+    const keys = await collect(store.keys());
+    return show('feed', {
+      topic, owner, root: store.root, records: keys.length, firstKeys: keys.slice(0, 3),
+      ms: Math.round(performance.now() - started), calls: provider.calls,
+    });
+  } catch (e) {
+    return show('feed', { ...failure(e), ms: Math.round(performance.now() - started), calls: provider.calls });
+  }
+}
+
+const RECORDS = () => ({
+  'check/small': { hello: 'Freedom', at: new Date().toISOString().slice(0, 10) },
+  'check/unicode/é😀': ['é', '中文', '😀'],
+  'check/large': 'x'.repeat(5000), // over one chunk: a leaf pair and an intermediate
+});
+
+async function write() {
+  $('status').textContent = 'writing… (approve Freedom’s prompts)';
+  const provider = counting(window.swarm);
+  const started = performance.now();
+  const records = RECORDS();
+  let expected;
+  try {
+    const offline = new RecordStore(new MemoryBytesStore({ addressing: 'swarm' }));
+    for (const [k, v] of Object.entries(records)) offline.put(k, v);
+    expected = await offline.commit();
+
+    const store = new RecordStore(new SwarmProviderBytesStore(provider));
+    for (const [k, v] of Object.entries(records)) store.put(k, v);
+    const root = await store.commit();
+    const wroteMs = Math.round(performance.now() - started);
+
+    // Read back through a fresh store: nothing cached from the write.
+    const back = RecordStore.at(root, new SwarmProviderBytesStore(window.swarm));
+    const readBack = await collect(back.items());
+    // Compare canonically: stored objects come back with their keys sorted.
+    const same = canonicalJson(readBack) === canonicalJson(Object.entries(records).sort((a, b) => (a[0] < b[0] ? -1 : 1)));
+    return show('write', {
+      root, expected, rootMatches: root === expected, readBackMatches: same,
+      wroteMs, totalMs: Math.round(performance.now() - started), calls: provider.calls,
+    });
+  } catch (e) {
+    return show('write', { ...failure(e), expected, ms: Math.round(performance.now() - started), calls: provider.calls });
+  }
+}
+
+window.rsCheck = {
+  results: {},
+  provider: Boolean(window.swarm && window.swarm.isFreedomBrowser),
+  read,
+  feed,
+  write,
+  ready: true,
+};
+$('read').onclick = () => read();
+$('write').onclick = () => write();
+$('status').textContent = window.rsCheck.provider ? 'ready (Freedom provider found)' : 'no window.swarm: open this page in Freedom Browser';
diff --git a/js/test/helpers.js b/js/test/helpers.js
new file mode 100644
index 0000000..1e547a2
--- /dev/null
+++ b/js/test/helpers.js
@@ -0,0 +1,34 @@
+import { readFileSync } from 'node:fs';
+import { parseJson } from '../src/canonical.js';
+import { MemoryBytesStore } from '../src/stores.js';
+
+/** A fixture written by tools/make_fixtures.py (big integers come back as BigInt). */
+export function fixture(name) {
+  return parseJson(readFileSync(new URL(`./fixtures/${name}`, import.meta.url), 'utf8'));
+}
+
+/** The deterministic test data the fixtures' Swarm addresses were computed over. */
+export function pattern(size) {
+  const out = new Uint8Array(size);
+  for (let i = 0; i < size; i++) out[i] = (i * 31 + 7) & 0xff;
+  return out;
+}
+
+export function storeFor(addressing) {
+  return new MemoryBytesStore({ addressing });
+}
+
+/** Apply fixture steps (put / delete / commit) to a store. */
+export async function apply(store, steps) {
+  for (const step of steps) {
+    if (step.op === 'put') store.put(step.key, step.value);
+    else if (step.op === 'delete') await store.delete(step.key);
+    else await store.commit();
+  }
+}
+
+export async function collect(asyncIterable) {
+  const out = [];
+  for await (const x of asyncIterable) out.push(x);
+  return out;
+}
diff --git a/js/test/live.test.js b/js/test/live.test.js
new file mode 100644
index 0000000..c4201f9
--- /dev/null
+++ b/js/test/live.test.js
@@ -0,0 +1,91 @@
+// Live checks against Swarm, through a node with Bee's API (Bee, or Ant
+// inside Freedom Browser). Skipped unless RECORDSTORE_BEE_URL is set:
+//
+//   RECORDSTORE_BEE_URL=http://localhost:1633 npm test
+//
+// Reading needs nothing else. RECORDSTORE_BEE_BATCH (a postage batch of the
+// node's) also runs the write check, which spends a few chunks of it.
+// Every blob and feed update read is checked against its reference/owner.
+
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { writeFileSync } from 'node:fs';
+import {
+  BeeBytesStore, RecordStore, SwarmFeedPointer, swarmStore, verifyFeedUpdate, verifyPrefix, verifyProof,
+} from '../src/index.js';
+import { toHex } from '../src/bytes.js';
+
+const url = process.env.RECORDSTORE_BEE_URL;
+const batch = process.env.RECORDSTORE_BEE_BATCH;
+const skip = !url && 'set RECORDSTORE_BEE_URL to run';
+const root = process.env.RECORDSTORE_LIVE_ROOT
+  || '67f6be2688e5fb7ca85a463c3d2929dad2b177bb3edb6bb910e8fa110598cff3'; // OntoDAG's `space` pack
+
+test('reads a store Python published on Swarm', { skip }, async () => {
+  const store = RecordStore.at(root, new BeeBytesStore(url, { concurrency: 32 }));
+  const started = Date.now();
+  const keys = [];
+  for await (const key of store.keys()) keys.push(key);
+  const sorted = [...keys].sort((a, b) => (Buffer.from(a) < Buffer.from(b) ? -1 : 1));
+  assert.ok(keys.length > 0, 'the store has records');
+  assert.deepEqual(keys, sorted, 'keys arrive in UTF-8 byte order');
+  assert.notEqual(await store.get(keys[Math.floor(keys.length / 2)]), undefined);
+  console.log(`# ${keys.length} keys in ${Date.now() - started} ms; e.g. ${JSON.stringify(keys.slice(0, 3))}`);
+});
+
+test("follows a store Python keeps behind a feed (loopmarket's test maker book)", { skip }, async () => {
+  const started = Date.now();
+  const store = await swarmStore('loop-twonode-1791730596', { url, owner: '300f4daa2653102abde4c66f75ea512c0bb81571' });
+  assert.match(store.root, /^[0-9a-f]{64}$/);
+  const items = [];
+  for await (const item of store.items()) items.push(item);
+  assert.ok(items.length > 0);
+  console.log(`# book root ${store.root.slice(0, 16)}..., ${items.length} records in ${Date.now() - started} ms; keys ${JSON.stringify(items.map(([k]) => k).slice(0, 6))}`);
+});
+
+test("reads the OntoDAG page's feed (written by Python's SwarmFeedPointer)", { skip }, async () => {
+  const pointer = new SwarmFeedPointer(url, 'ontodag/freedom-page', { owner: 'e6d5bbdd5028b84d5eaf7be5fe07bed1ad73a110' });
+  assert.equal(pointer.topic, '19e8777d991f320d8d8158a4016858ad2f9c66b89abf0f9393ef239fabbbf2e5');
+  const ref = await pointer.get();
+  assert.match(ref, /^[0-9a-f]{64}$/);
+  const updates = await pointer.updates();
+  const last = verifyFeedUpdate(updates.at(-1), pointer.owner, 'ontodag/freedom-page');
+  assert.equal(last.root, ref);
+  console.log(`# page feed: ${updates.length} updates, latest ${ref.slice(0, 16)}... at ${new Date(last.timestamp * 1000).toISOString()}`);
+});
+
+test('publishes a store behind a new feed and follows it', { skip: skip || (!batch && 'set RECORDSTORE_BEE_BATCH to run the write check') }, async () => {
+  const signer = toHex(crypto.getRandomValues(new Uint8Array(32)));
+  const topic = `recordstore-js-live-${Date.now()}`;
+  const writer = await swarmStore(topic, { url, signer, batch });
+  assert.equal(writer.root, null);
+  writer.put('a', { n: 1 });
+  writer.put('b', 'two');
+  const first = await writer.commit();
+  writer.put('c', [3]);
+  await writer.delete('a');
+  const second = await writer.commit();
+  const follower = await swarmStore(topic, { url, owner: writer._pointer.owner });
+  assert.equal(follower.root, second);
+  const items = [];
+  for await (const item of follower.items()) items.push(item);
+  assert.deepEqual(items, [['b', 'two'], ['c', [3]]]);
+  const updates = await follower._pointer.updates();
+  assert.deepEqual(updates.map((u) => verifyFeedUpdate(u, writer._pointer.owner, topic).root), [first, second]);
+  console.log(`# wrote feed ${topic} (owner ${writer._pointer.owner}): roots ${first.slice(0, 12)}..., ${second.slice(0, 12)}...`);
+});
+
+test('proves records of a published store, checkable with no node', { skip }, async () => {
+  const store = RecordStore.at(root, new BeeBytesStore(url, { concurrency: 32 }));
+  const started = Date.now();
+  const proof = await store.prove('abbey');
+  assert.deepEqual(await verifyProof(proof, root), await store.get('abbey'));
+  const absent = await store.prove('abbeyz');
+  assert.equal(absent.present, false);
+  const prefix = await store.provePrefix('ab');
+  const records = await verifyPrefix(prefix, root);
+  assert.ok(records.size > 0);
+  if (process.env.RECORDSTORE_PROOF_OUT) writeFileSync(process.env.RECORDSTORE_PROOF_OUT, JSON.stringify({ root, proof, absent, prefix }));
+  const size = (p) => JSON.stringify(p).length;
+  console.log(`# key proof ${proof.nodes.length} nodes / ${size(proof)} B, absence ${size(absent)} B, prefix 'ab' ${records.size} records / ${size(prefix)} B, ${Date.now() - started} ms`);
+});
diff --git a/js/test/merge.test.js b/js/test/merge.test.js
new file mode 100644
index 0000000..6d6f2e9
--- /dev/null
+++ b/js/test/merge.test.js
@@ -0,0 +1,42 @@
+// Cross-language: three-way merges reach Python's merged roots, and
+// conflicts are the same keys.
+
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { ABSENT, DELETE, MergeConflict, RecordStore } from '../src/recordstore.js';
+import { apply, fixture, storeFor } from './helpers.js';
+
+const { cases } = fixture('merges.json');
+
+const oursWins = (_key, _base, ours) => (ours === ABSENT ? DELETE : ours);
+
+for (const c of cases) {
+  test(`merge ${c.name}`, async () => {
+    const blobs = storeFor(c.addressing);
+    const baseStore = new RecordStore(blobs);
+    await apply(baseStore, c.base);
+    assert.equal(baseStore.root, c.roots.base);
+    const branch = async (steps) => {
+      const s = new RecordStore(blobs, { root: c.roots.base });
+      await apply(s, steps);
+      return s.commit();
+    };
+    const ours = await branch(c.ours);
+    const theirs = await branch(c.theirs);
+    assert.equal(ours, c.roots.ours);
+    assert.equal(theirs, c.roots.theirs);
+
+    if (c.merged !== undefined) {
+      assert.equal(await RecordStore.merge(blobs, c.roots.base, ours, theirs), c.merged);
+      // Commutative without a resolver.
+      assert.equal(await RecordStore.merge(blobs, c.roots.base, theirs, ours), c.merged);
+    } else {
+      await assert.rejects(RecordStore.merge(blobs, c.roots.base, ours, theirs), (e) => {
+        assert.ok(e instanceof MergeConflict);
+        assert.deepEqual(e.conflicts.slice().sort(), c.conflicts);
+        return true;
+      });
+      assert.equal(await RecordStore.merge(blobs, c.roots.base, ours, theirs, oursWins), c.merged_ours_wins);
+    }
+  });
+}
diff --git a/js/test/proofs.test.js b/js/test/proofs.test.js
new file mode 100644
index 0000000..695039a
--- /dev/null
+++ b/js/test/proofs.test.js
@@ -0,0 +1,90 @@
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import {
+  ABSENT, MemoryBytesStore, ProofError, RecordStore, verifyExtension, verifyPrefix, verifyProof,
+} from '../src/index.js';
+import { apply, fixture } from './helpers.js';
+
+const { cases } = fixture('proofs.json');
+
+async function build(c) {
+  const blobs = new MemoryBytesStore({ addressing: c.addressing });
+  const store = new RecordStore(blobs);
+  await apply(store, c.first);
+  assert.equal(store.root, c.roots.a);
+  await apply(store, c.second);
+  assert.equal(store.root, c.roots.b);
+  return blobs;
+}
+
+for (const c of cases) {
+  const opts = { addressing: c.addressing };
+
+  test(`${c.addressing}: key proofs are identical to Python's and verify to the same records`, async () => {
+    const blobs = await build(c);
+    const empty = new RecordStore(new MemoryBytesStore(opts));
+    assert.deepEqual(await empty.prove(c.empty.key, opts), c.empty.proof);
+    assert.equal(await verifyProof(c.empty.proof, null), ABSENT);
+    for (const k of c.keys) {
+      const store = RecordStore.at(k.proof.root, blobs);
+      assert.deepEqual(await store.prove(k.key, opts), k.proof, `prove(${JSON.stringify(k.key)})`);
+      const verdict = await verifyProof(k.proof, k.proof.root);
+      if (k.present) assert.deepEqual(verdict, k.value);
+      else assert.equal(verdict, ABSENT);
+    }
+  });
+
+  test(`${c.addressing}: prefix proofs are identical and complete`, async () => {
+    const blobs = await build(c);
+    for (const p of c.prefixes) {
+      const store = RecordStore.at(p.proof.root, blobs);
+      assert.deepEqual(await store.provePrefix(p.prefix, opts), p.proof, `provePrefix(${JSON.stringify(p.prefix)})`);
+      assert.deepEqual([...(await verifyPrefix(p.proof, p.proof.root))], p.records);
+    }
+  });
+
+  test(`${c.addressing}: extension proofs are identical, and the same extensions are refused`, async () => {
+    const blobs = await build(c);
+    for (const e of c.extensions) {
+      const store = RecordStore.at(e.root, blobs);
+      assert.equal(await store.extends(e.base, e.prefixes), e.extends);
+      if (e.proof) {
+        assert.deepEqual(await store.proveExtension(e.base, e.prefixes, opts), e.proof);
+        assert.deepEqual(await verifyExtension(e.proof, e.base, e.root), e.proven);
+      } else {
+        const named = e.error.match(/: '(.+)' is (absent from|changed in) it$/);
+        await assert.rejects(store.proveExtension(e.base, e.prefixes, opts), (err) => {
+          assert.ok(err.message.includes(`${JSON.stringify(named[1])} is ${named[2]} it`), err.message);
+          return true;
+        });
+      }
+    }
+  });
+
+  test(`${c.addressing}: every proof Python refuses is refused`, async () => {
+    for (const r of c.refused) {
+      const check = r.kind === 'key' ? verifyProof(r.envelope, r.root)
+        : r.kind === 'prefix' ? verifyPrefix(r.envelope, r.root)
+          : verifyExtension(r.envelope, r.base, r.root);
+      await assert.rejects(check, ProofError, r.name);
+    }
+  });
+}
+
+test('proofs are about committed roots: staged keys are refused', async () => {
+  const store = new RecordStore(new MemoryBytesStore());
+  store.put('a/1', 1);
+  await store.commit();
+  store.put('a/2', 2);
+  await assert.rejects(store.prove('a/2'), /staged/);
+  await assert.rejects(store.provePrefix('a/'), /staged/);
+  await assert.rejects(store.proveExtension(null, ['a/']), /staged/);
+  assert.deepEqual(await verifyProof(await store.prove('a/1'), store.root), 1);
+});
+
+test('a store with its own addressing function must name the scheme', async () => {
+  const blobs = new MemoryBytesStore({ addressing: async (d) => (await crypto.subtle.digest('SHA-256', d), 'x'.repeat(64)) });
+  const store = RecordStore.at(null, blobs);
+  await assert.rejects(store.prove('k'), /addressing/);
+  assert.equal((await store.prove('k', { addressing: 'sha256' })).present, false);
+});
diff --git a/js/test/provider.test.js b/js/test/provider.test.js
new file mode 100644
index 0000000..824c9c2
--- /dev/null
+++ b/js/test/provider.test.js
@@ -0,0 +1,105 @@
+// SwarmProviderBytesStore against a fake window.swarm provider, offline.
+// test/freedom/freedom_check.cjs runs the same store in real Freedom.
+
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { MemoryBytesStore, RecordStore, SwarmProviderBytesStore, VerificationError } from '../src/index.js';
+import { chunkAddress, encodeSpan } from '../src/address.js';
+import { concatBytes, toHex } from '../src/bytes.js';
+import { collect } from './helpers.js';
+
+/**
+ * A provider that publishes chunks under their BMT address and serves
+ * whole blobs by joining them, as Freedom's node does. It records the order
+ * of requests and how many publishes are in flight at once.
+ */
+function fakeProvider({ corrupt = false } = {}) {
+  const chunks = new Map(); // address -> {span, payload}
+  const log = [];
+  let inFlight = 0;
+  let maxInFlightBeforeFirstDone = 0;
+  let firstDone = false;
+  const join = (address) => {
+    const c = chunks.get(address);
+    if (!c) throw Object.assign(new Error('not found'), { data: { reason: 'chunk_not_found' } });
+    if (c.span <= 4096) return c.payload;
+    const parts = [];
+    for (let i = 0; i < c.payload.length; i += 32) parts.push(join(toHex(c.payload.subarray(i, i + 32))));
+    return concatBytes(...parts);
+  };
+  return {
+    log,
+    get maxInFlightBeforeFirstDone() { return maxInFlightBeforeFirstDone; },
+    async request({ method, params }) {
+      log.push(method);
+      if (method === 'swarm_requestAccess') return { connected: true };
+      if (method === 'swarm_publishChunk') {
+        inFlight++;
+        if (!firstDone) maxInFlightBeforeFirstDone = Math.max(maxInFlightBeforeFirstDone, inFlight);
+        await new Promise((r) => setTimeout(r, 1));
+        const payload = Uint8Array.from(params.data);
+        const span = params.span ?? payload.length;
+        const address = toHex(chunkAddress(concatBytes(encodeSpan(span), payload)));
+        chunks.set(address, { span, payload });
+        inFlight--;
+        firstDone = true;
+        return { reference: corrupt ? '00'.repeat(32) : address };
+      }
+      if (method === 'swarm_readData') {
+        const data = join(params.reference);
+        return { data: btoa(String.fromCharCode(...data)), encoding: 'base64' };
+      }
+      throw new Error(`unexpected ${method}`);
+    },
+  };
+}
+
+test('a store written through the provider has the offline Swarm root, and reads back', async () => {
+  const provider = fakeProvider();
+  const records = { small: { a: 1 }, large: 'x'.repeat(9000), 'é😀': [1, 2] };
+  const offline = new RecordStore(new MemoryBytesStore({ addressing: 'swarm' }));
+  const store = new RecordStore(new SwarmProviderBytesStore(provider));
+  for (const [k, v] of Object.entries(records)) {
+    offline.put(k, v);
+    store.put(k, v);
+  }
+  const root = await store.commit();
+  assert.equal(root, await offline.commit());
+  const back = RecordStore.at(root, new SwarmProviderBytesStore(provider));
+  assert.deepEqual(await collect(back.items()), [['large', records.large], ['small', { a: 1 }], ['é😀', [1, 2]]]);
+  assert.equal(provider.log[0], 'swarm_requestAccess');
+});
+
+test('the first chunk is published alone, so one "Always allow" covers the rest', async () => {
+  const provider = fakeProvider();
+  const store = new RecordStore(new SwarmProviderBytesStore(provider, { concurrency: 4 }));
+  for (let i = 0; i < 20; i++) store.put(`k${i}`, 'v'.repeat(5000)); // many chunks, published in parallel
+  await store.commit();
+  assert.equal(provider.maxInFlightBeforeFirstDone, 1);
+  assert.equal(provider.log.filter((m) => m === 'swarm_requestAccess').length, 1);
+});
+
+test('a reference the provider got wrong is caught', async () => {
+  const blobs = new SwarmProviderBytesStore(fakeProvider({ corrupt: true }));
+  await assert.rejects(blobs.put(new Uint8Array([1, 2, 3])), VerificationError);
+});
+
+test('a refused connection is asked again on the next write', async () => {
+  let asked = 0;
+  const provider = {
+    async request({ method }) {
+      if (method === 'swarm_requestAccess') {
+        asked++;
+        if (asked === 1) throw Object.assign(new Error('User rejected'), { code: 4001 });
+        return { connected: true };
+      }
+      return fakeProviderPublish(method);
+    },
+  };
+  const fallback = fakeProvider();
+  const fakeProviderPublish = (method) => fallback.request({ method, params: { data: new Uint8Array([7]) } });
+  const blobs = new SwarmProviderBytesStore(provider);
+  await assert.rejects(blobs.put(new Uint8Array([7])), /rejected/);
+  assert.match(await blobs.put(new Uint8Array([7])), /^[0-9a-f]{64}$/);
+  assert.equal(asked, 2);
+});
diff --git a/js/test/store.test.js b/js/test/store.test.js
new file mode 100644
index 0000000..8240f3e
--- /dev/null
+++ b/js/test/store.test.js
@@ -0,0 +1,48 @@
+// Cross-language: replay the operation sequences Python ran and reach the
+// same root at every commit, then read stores Python wrote.
+
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { RecordStore } from '../src/recordstore.js';
+import { fromHex } from '../src/bytes.js';
+import { collect, fixture, storeFor } from './helpers.js';
+
+const { cases } = fixture('stores.json');
+
+for (const c of cases) {
+  test(`${c.name}: every commit reaches Python's root`, async () => {
+    const store = new RecordStore(storeFor(c.addressing));
+    let commit = 0;
+    for (const step of c.steps) {
+      if (step.op === 'put') {
+        store.put(step.key, step.value);
+      } else if (step.op === 'delete') {
+        const present = await store.has(step.key);
+        assert.equal(await store.delete(step.key), present);
+      } else {
+        commit++;
+        assert.equal(await store.commit(), step.root, `commit ${commit}`);
+      }
+    }
+    assert.deepEqual(await collect(store.items()), c.items);
+  });
+}
+
+test('reads a store Python wrote, from its blobs alone', async () => {
+  for (const c of cases.filter((x) => x.blobs)) {
+    const blobs = storeFor(c.addressing);
+    for (const [ref, hex] of Object.entries(c.blobs)) {
+      assert.equal(await blobs.put(fromHex(hex)), ref, 'the blob hashes to its reference');
+    }
+    const commits = c.steps.filter((s) => s.op === 'commit');
+    // The fourth commit holds the unicode keys; read it as a snapshot.
+    const snapshot = RecordStore.at(commits[3].root, blobs);
+    const items = await collect(snapshot.items());
+    assert.deepEqual(items.map(([k]) => k), ['a/b/c', 'users/alice', 'users/bob', 'userz', 'é', '￿', '😀']);
+    assert.deepEqual(await snapshot.get('users/alice'), { name: 'Alice', role: 'admin' });
+    assert.equal(await snapshot.get('a/b/c'), null);
+    assert.equal(await snapshot.get('u'), undefined);
+    const last = RecordStore.at(commits.at(-1).root, blobs);
+    assert.deepEqual(await collect(last.items()), c.items);
+  }
+});
diff --git a/js/tools/make_fixtures.py b/js/tools/make_fixtures.py
new file mode 100644
index 0000000..5f26c0e
--- /dev/null
+++ b/js/tools/make_fixtures.py
@@ -0,0 +1,591 @@
+#!/usr/bin/env python3
+"""Generate the cross-language fixtures the JavaScript port must reproduce.
+
+The Python package is the reference implementation of recordstore's wire
+format. This script runs it (the source in ``src/``, not an installed copy)
+and writes what it produces to ``js/test/fixtures/``: canonical encodings,
+Swarm content addresses, the root after every commit of scripted and random
+stores, and three-way merges. The JavaScript tests replay the same operations
+and must reach byte-identical bytes and roots.
+
+    python3 js/tools/make_fixtures.py           # rewrite the fixtures
+    python3 js/tools/make_fixtures.py --check   # exit 1 if they are stale
+
+Needs swarmfs (with keccak) for the Swarm-addressed cases, as
+``addressing="swarm"`` does.
+"""
+
+from __future__ import annotations
+
+import json
+import os
+import random
+import sys
+
+HERE = os.path.dirname(os.path.abspath(__file__))
+REPO = os.path.dirname(os.path.dirname(HERE))
+OUT = os.path.join(REPO, "js", "test", "fixtures")
+sys.path.insert(0, os.path.join(REPO, "src"))
+
+from recordstore import (  # noqa: E402  (the path insert must come first)
+    ABSENT, DELETE, MemoryBytesStore, MergeConflict, RecordStore,
+    canonical_bytes,
+)
+from recordstore.recordstore import _encode_value, _swarm_ref  # noqa: E402
+
+
+class SwarmMemoryStore:
+    """MemoryBytesStore under Swarm (BMT) addressing: the references a Bee
+    node gives these blobs, computed offline."""
+
+    def __init__(self):
+        self.blobs = {}
+
+    def put(self, data: bytes) -> str:
+        ref = _swarm_ref(data)
+        self.blobs[ref] = data
+        return ref
+
+    def get(self, ref: str) -> bytes:
+        try:
+            return self.blobs[ref]
+        except KeyError:
+            raise KeyError(f"reference not found: {ref}") from None
+
+
+def new_store(addressing: str):
+    return SwarmMemoryStore() if addressing == "swarm" else MemoryBytesStore()
+
+
+# ---------------------------------------------------------------------------
+# Canonical encoding
+# ---------------------------------------------------------------------------
+
+def canonical_cases():
+    """Values whose canonical bytes the JavaScript encoder must match.
+
+    Integral floats below 2**53 (``1.0``) are left out on purpose: JavaScript
+    has one number type and writes them as integers (see the JS README).
+    """
+    strings = [
+        "", "plain", 'quote " and backslash \\', "tab\tnewline\ncr\r",
+        "controls \x00\x01\x1f\x7f", "\b\f", "é 中文 😀", "

",
+        "￿", "/", "",
+    ]
+    ints = [0, 1, -1, 42, 2**31, -(2**31) - 1, 2**53 - 1, -(2**53 - 1),
+            2**53, 2**63, -(2**70), 10**30]
+    floats = [0.1, -2.5, 0.30000000000000004, 1.0000000000000002, 123.456,
+              1e-4, 0.0001234, 1e-5, 1e-7, 2.5e-5, 1.5e-300, 5e-324,
+              1e15 + 0.5, 1e16, 1e21, 1e22, 1.7976931348623157e308,
+              9007199254740992.0, -1e100, 3.14159265358979, 100.5]
+    objects = [
+        {},
+        [],
+        {"b": 1, "a": 2},
+        {"z": 1, "é": 2, "😀": 3, "￿": 4, "A": 5, "a": 6, "": 7,
+         "aa": 8},
+        {"nested": {"list": [1, "two", None, True, False, {"x": []}],
+                    "empty": {}}},
+        [None, True, False, 0, "", [], {}],
+        {"users": [{"name": "Alice", "role": "admin"},
+                   {"name": "Bob", "tags": ["x", "y"]}]},
+    ]
+    values = ([None, True, False] + strings + ints + floats + objects)
+    return [{"value": v, "hex": canonical_bytes(v).hex()} for v in values]
+
+
+def value_blob_cases():
+    """The record envelope ``{"rsv":1,"val":...}`` around a few values."""
+    return [{"value": v, "hex": _encode_value(v).hex()}
+            for v in [None, "x", 1, 0.5, {"a": [1, 2]}]]
+
+
+# ---------------------------------------------------------------------------
+# Swarm content addresses
+# ---------------------------------------------------------------------------
+
+def pattern(size: int) -> bytes:
+    """Deterministic test data; the JS tests rebuild it the same way."""
+    return bytes(((i * 31) + 7) & 0xFF for i in range(size))
+
+
+def swarm_cases():
+    sizes = [0, 1, 31, 32, 33, 64, 4095, 4096, 4097, 8192, 8193,
+             4096 * 128, 4096 * 128 + 1, 4096 * 129, 4096 * 256 + 5]
+    return [{"size": n, "ref": _swarm_ref(pattern(n))} for n in sizes]
+
+
+# ---------------------------------------------------------------------------
+# Stores: scripted and random operation sequences
+# ---------------------------------------------------------------------------
+
+def scripted_ops():
+    """Shared prefixes, splits and the merges a delete forces."""
+    return [
+        ("put", "users/alice", {"name": "Alice", "role": "admin"}),
+        ("put", "users/bob", {"name": "Bob"}),
+        ("put", "users/al", "a proper prefix of alice"),
+        ("put", "u", 1),
+        ("put", "userz", [1, 2, 3]),
+        ("commit",),
+        ("delete", "users/al"),
+        ("commit",),
+        ("put", "users/alice", {"name": "Alice", "role": "admin"}),  # same value
+        ("commit",),
+        ("put", "é", "two bytes"),
+        ("put", "😀", "four bytes"),
+        ("put", "￿", "three bytes, sorts before the emoji"),
+        ("put", "a/b/c", None),
+        ("delete", "u"),
+        ("delete", "never-was"),
+        ("commit",),
+        ("put", "tmp", "staged then deleted"),
+        ("delete", "tmp"),
+        ("put", "users/bob", {"name": "Bob", "age": 0.5}),
+        ("commit",),
+        ("delete", "users/alice"),
+        ("delete", "users/bob"),
+        ("delete", "userz"),
+        ("commit",),
+    ]
+
+
+SEGMENTS = ["users", "u", "user", "a", "ab", "abc", "é", "😀", "￿", "x"]
+ALPHABET = list("abcxyz019 /\"\\") + ["\n", "\t", "\x01", "é", "中", "😀",
+                                       "￿", "
"]
+
+
+def random_key(rng):
+    if rng.random() < 0.2:
+        return rng.choice(SEGMENTS)
+    return "/".join(rng.choice(SEGMENTS) for _ in range(rng.randint(1, 3)))
+
+
+def random_string(rng):
+    return "".join(rng.choice(ALPHABET) for _ in range(rng.randint(0, 8)))
+
+
+def random_float(rng):
+    while True:
+        f = rng.choice([rng.uniform(-1e6, 1e6), rng.uniform(-1, 1),
+                        rng.uniform(1, 10) * 10 ** rng.randint(-12, 25)])
+        if not f.is_integer():
+            return f
+
+
+def random_value(rng, depth=0):
+    kind = rng.randint(0, 8 if depth < 2 else 5)
+    if kind == 0:
+        return None
+    if kind == 1:
+        return rng.random() < 0.5
+    if kind == 2:
+        return rng.randint(-(2**53 - 1), 2**53 - 1)
+    if kind == 3:
+        return random_float(rng)
+    if kind == 4:
+        return random_string(rng)
+    if kind == 5:
+        return rng.choice([2**60 + rng.randint(0, 999), -(2**64)])
+    if kind in (6, 7):
+        return {random_string(rng): random_value(rng, depth + 1)
+                for _ in range(rng.randint(0, 4))}
+    return [random_value(rng, depth + 1) for _ in range(rng.randint(0, 4))]
+
+
+def random_ops(seed, count):
+    rng = random.Random(seed)
+    ops, live = [], set()
+    for _ in range(count):
+        roll = rng.random()
+        if roll < 0.6:
+            key = random_key(rng)
+            ops.append(("put", key, random_value(rng)))
+            live.add(key)
+        elif roll < 0.85:
+            if live and rng.random() < 0.85:
+                key = rng.choice(sorted(live))
+                live.discard(key)
+            else:
+                key = random_key(rng)
+                live.discard(key)
+            ops.append(("delete", key))
+        else:
+            ops.append(("commit",))
+    ops.append(("commit",))
+    return ops
+
+
+def run(ops, addressing):
+    """Replay `ops` on the Python store; return the steps as fixture data
+    (each commit carrying the root it must produce, each delete whether the
+    key existed) and the final store."""
+    blobs = new_store(addressing)
+    store = RecordStore(blobs)
+    steps = []
+    for op in ops:
+        if op[0] == "put":
+            store.put(op[1], op[2])
+            steps.append({"op": "put", "key": op[1], "value": op[2]})
+        elif op[0] == "delete":
+            try:
+                store.delete(op[1])
+                existed = True
+            except KeyError:
+                existed = False
+            steps.append({"op": "delete", "key": op[1], "existed": existed})
+        else:
+            steps.append({"op": "commit", "root": store.commit()})
+    return steps, store, blobs
+
+
+def store_cases():
+    cases = []
+    for name, ops in [("scripted", scripted_ops()),
+                      ("random-1", random_ops(1, 400)),
+                      ("random-2", random_ops(2, 400))]:
+        for addressing in ("sha256", "swarm"):
+            steps, store, blobs = run(ops, addressing)
+            case = {
+                "name": f"{name}/{addressing}",
+                "addressing": addressing,
+                "steps": steps,
+                "items": [[k, v] for k, v in store.items()],
+            }
+            if name == "scripted":
+                # Every blob, so the JS reader is tested on bytes Python wrote.
+                case["blobs"] = {ref: data.hex()
+                                 for ref, data in sorted(blobs.blobs.items())}
+            cases.append(case)
+    return cases
+
+
+# ---------------------------------------------------------------------------
+# Three-way merges
+# ---------------------------------------------------------------------------
+
+def ours_wins(_key, _base, ours, _theirs):
+    """The resolver the JS tests implement identically."""
+    return DELETE if ours is ABSENT else ours
+
+
+def merge_cases():
+    base_ops = [("put", f"k{i}", {"n": i}) for i in range(20)] + [
+        ("put", "shared/a", 1), ("put", "shared/b", 2), ("commit",)]
+    scenarios = [
+        ("disjoint",
+         [("put", "k1", "ours"), ("delete", "k2"), ("put", "new/ours", 1)],
+         [("put", "k3", "theirs"), ("delete", "k4"), ("put", "new/theirs", 2)]),
+        ("same-change",
+         [("put", "k5", "both"), ("delete", "k6")],
+         [("put", "k5", "both"), ("delete", "k6"), ("put", "k7", "theirs")]),
+        ("conflict",
+         [("put", "k8", "ours"), ("delete", "shared/a"), ("put", "x", 1)],
+         [("put", "k8", "theirs"), ("put", "shared/a", 99), ("put", "x", 2)]),
+    ]
+    cases = []
+    for addressing in ("sha256", "swarm"):
+        for name, ours_ops, theirs_ops in scenarios:
+            blobs = new_store(addressing)
+            base_store = RecordStore(blobs)
+            for op in base_ops:
+                _apply(base_store, op)
+            base = base_store.root
+
+            def branch(ops):
+                s = RecordStore(blobs, root=base)
+                for op in ops:
+                    _apply(s, op)
+                return s.commit()
+
+            ours, theirs = branch(ours_ops), branch(theirs_ops)
+            case = {"name": f"{name}/{addressing}", "addressing": addressing,
+                    "base": [_op_json(op) for op in base_ops],
+                    "ours": [_op_json(op) for op in ours_ops],
+                    "theirs": [_op_json(op) for op in theirs_ops],
+                    "roots": {"base": base, "ours": ours, "theirs": theirs}}
+            try:
+                case["merged"] = RecordStore.merge(blobs, base, ours, theirs)
+            except MergeConflict as e:
+                case["conflicts"] = sorted(e.conflicts)
+                case["merged_ours_wins"] = RecordStore.merge(
+                    blobs, base, ours, theirs, resolver=ours_wins)
+            cases.append(case)
+    return cases
+
+
+def _apply(store, op):
+    if op[0] == "put":
+        store.put(op[1], op[2])
+    elif op[0] == "delete":
+        store.delete(op[1])
+    else:
+        store.commit()
+
+
+def _op_json(op):
+    if op[0] == "put":
+        return {"op": "put", "key": op[1], "value": op[2]}
+    if op[0] == "delete":
+        return {"op": "delete", "key": op[1]}
+    return {"op": "commit"}
+
+
+# ---------------------------------------------------------------------------
+# Feeds: topics, owners, addresses, signed updates (needs swarmfs[feeds])
+# ---------------------------------------------------------------------------
+
+KEYS = ["11" * 32, "a7" * 31 + "01"]
+TOPIC_NAMES = ["recordstore/test", "loop-twonode-1791730596", "ab" * 32, "é😀"]
+
+
+def pointer_for(name, key):
+    """The Python pointer, for how it hashes topics and derives owners (no
+    network is touched until it is used)."""
+    from recordstore import SwarmFeedPointer
+    return SwarmFeedPointer("http://127.0.0.1:9", name, signer=key)
+
+
+def signed_update(key, topic_hex, index, root, timestamp):
+    """A feed update exactly as swarmfs's FeedOps.update publishes it (its
+    own code, with the clock fixed and the upload captured)."""
+    import asyncio
+    from unittest import mock
+    from swarmfs.feeds import FeedOps, FeedSigner, feed_identifier, soc_address
+
+    captured = {}
+
+    class Capture:
+        async def soc_post(self, owner, identifier, signature, data, stamp):
+            captured.update(owner=owner, identifier=identifier,
+                            signature=signature, data=bytes(data))
+            return "00" * 32
+
+    signer = FeedSigner(key)
+    with mock.patch("swarmfs.feeds.time.time", return_value=float(timestamp)):
+        asyncio.run(FeedOps(Capture()).update(
+            signer, bytes.fromhex(topic_hex), index, root, "00" * 32))
+    identifier = bytes.fromhex(captured["identifier"])
+    soc = identifier + bytes.fromhex(captured["signature"]) + captured["data"]
+    return {
+        "key": key, "topic": topic_hex, "index": index, "root": root,
+        "timestamp": timestamp,
+        "identifier": captured["identifier"],
+        "address": soc_address(feed_identifier(bytes.fromhex(topic_hex), index),
+                               signer.owner).hex(),
+        "soc": soc.hex(),
+    }
+
+
+def feed_cases():
+    from recordstore import FEED_UPDATE_FORMAT, ProofError, verify_equivocation, verify_feed_update
+    from swarmfs.feeds import feed_identifier, soc_address
+
+    owners = [{"key": k, "owner": pointer_for("x", k).owner} for k in KEYS]
+    topics = [{"name": n, "topic": pointer_for(n, KEYS[0]).topic} for n in TOPIC_NAMES]
+    t0 = topics[0]["topic"]
+    owner0 = bytes.fromhex(owners[0]["owner"])
+    addresses = []
+    for index in (0, 1, 255, 2**32 + 5):
+        ident = feed_identifier(bytes.fromhex(t0), index)
+        addresses.append({"topic": t0, "owner": owners[0]["owner"], "index": index,
+                          "identifier": ident.hex(),
+                          "address": soc_address(ident, owner0).hex()})
+
+    root_a = "5a" * 32
+    root_b = "c3" * 32
+    root_64 = "77" * 64  # an encrypted reference: 64 bytes
+    updates = [signed_update(KEYS[0], t0, 0, root_a, 1_790_000_000),
+               signed_update(KEYS[0], t0, 7, root_b, 1_790_000_123),
+               signed_update(KEYS[1], topics[1]["topic"], 3, root_a, 1_800_000_000),
+               signed_update(KEYS[0], t0, 7, root_a, 1_790_000_124),   # equivocates with [1]
+               signed_update(KEYS[0], t0, 9, root_64, 1_790_000_200)]
+    for u in updates:
+        owner = pointer_for("x", u["key"]).owner
+        envelope = {"format": FEED_UPDATE_FORMAT, "version": 1, "owner": owner,
+                    "topic": u["topic"], "index": u["index"], "soc": u["soc"]}
+        verified = verify_feed_update(envelope, owner, u["topic"])
+        u.update(owner=owner, envelope=envelope,
+                 verified={"index": verified.index, "root": verified.root,
+                           "timestamp": verified.timestamp})
+    equivocation_index = verify_equivocation(updates[1]["envelope"], updates[3]["envelope"],
+                                             updates[1]["owner"], t0)
+    # Each must be refused by Python, and so by JavaScript.
+    bad = []
+    for name, tamper in [
+        ("signature byte flipped", lambda e: {**e, "soc": e["soc"][:66] + ("00" if e["soc"][66:68] != "00" else "01") + e["soc"][68:]}),
+        ("payload byte flipped", lambda e: {**e, "soc": e["soc"][:-2] + ("00" if e["soc"][-2:] != "00" else "01")}),
+        ("claims another index", lambda e: {**e, "index": e["index"] + 1}),
+        ("claims another owner", lambda e: {**e, "owner": "00" * 20}),
+    ]:
+        envelope = tamper(updates[0]["envelope"])
+        try:
+            verify_feed_update(envelope, envelope["owner"], t0)
+            raise AssertionError(f"Python accepted a tampered update: {name}")
+        except ProofError:
+            bad.append({"name": name, "envelope": envelope})
+    return {"owners": owners, "topics": topics, "addresses": addresses,
+            "updates": updates, "equivocation": {"a": 1, "b": 3, "index": equivocation_index},
+            "refused": bad}
+
+
+# ---------------------------------------------------------------------------
+# Proofs: key inclusion and absence, prefixes, extensions
+# ---------------------------------------------------------------------------
+
+def proof_ops():
+    """A store that changes in two steps: `log/` only grows, other keys
+    change, so the second root extends the first under 'log/' and not
+    under ''."""
+    first = [("put", f"log/{i}", {"event": i}) for i in range(1, 6)] + [
+        ("put", "state/x", "old"), ("put", "state/y", [1, 2]), ("put", "é", "é"),
+        ("put", "😀", None), ("put", "users/alice", {"n": "Alice"}),
+        ("put", "users/al", 0.5), ("commit",)]
+    second = [("put", "log/6", {"event": 6}), ("put", "state/x", "new"),
+              ("delete", "users/al"), ("put", "zz", True), ("commit",)]
+    return first, second
+
+
+def proof_cases():
+    from recordstore import ABSENT, ProofError, verify_extension, verify_prefix, verify_proof
+
+    cases = []
+    first, second = proof_ops()
+    keys = ["log/3", "state/x", "users/alice", "users/al", "users/a", "users/alicex",
+            "é", "😀", "nope", "log/", "zzz"]
+    prefixes = ["", "log/", "users/", "users/al", "state/", "é", "zz", "x"]
+    for addressing in ("sha256", "swarm"):
+        blobs = new_store(addressing)
+        store = RecordStore(blobs)
+        empty = {"key": "anything", "proof": store.prove("anything", addressing=addressing)}
+        for op in first:
+            _apply(store, op)
+        root_a = store.root
+        for op in second:
+            _apply(store, op)
+        root_b = store.root
+        a = RecordStore.at(root_a, blobs)
+        b = RecordStore.at(root_b, blobs)
+
+        def key_proof(s, k):
+            p = s.prove(k, addressing=addressing)
+            v = verify_proof(p, s.root)
+            return {"key": k, "proof": p, "present": v is not ABSENT,
+                    "value": None if v is ABSENT else v}
+
+        def prefix_proof(s, pre):
+            p = s.prove_prefix(pre, addressing=addressing)
+            return {"prefix": pre, "proof": p, "records": sorted(verify_prefix(p, s.root).items())}
+
+        extensions = []
+        for base, later, pres in [(root_a, root_b, ["log/"]), (root_a, root_b, ["log/", "é", "😀"]),
+                                  (None, root_b, [""]), (root_b, root_b, [""]),
+                                  (root_a, root_b, [""]), (root_a, root_b, ["state/"]),
+                                  (root_a, root_b, ["users/"])]:
+            s = RecordStore.at(later, blobs)
+            entry = {"base": base, "root": later, "prefixes": pres,
+                     "extends": s.extends(base, pres)}
+            try:
+                p = s.prove_extension(base, pres, addressing=addressing)
+                entry["proof"] = p
+                entry["proven"] = list(verify_extension(p, base, later))
+            except ValueError as e:
+                entry["error"] = str(e)
+            extensions.append(entry)
+
+        refused = []
+        good = key_proof(b, "log/3")["proof"]
+        node0 = good["nodes"][0]
+        for name, envelope, against in [
+            ("a node byte changed", {**good, "nodes": [node0[:-2] + ("00" if node0[-2:] != "00" else "01")] + good["nodes"][1:]}, root_b),
+            ("the value changed", {**good, "value": good["value"][:-4] + "3030"}, root_b),
+            ("claims absence", {**good, "present": False, "value": None}, root_b),
+            ("about another root", good, root_a),
+            ("the last node dropped", {**good, "nodes": good["nodes"][:-1]}, root_b),
+        ]:
+            try:
+                verify_proof(envelope, against)
+                raise AssertionError(f"Python accepted a tampered proof: {name}")
+            except ProofError:
+                refused.append({"kind": "key", "name": name, "envelope": envelope, "root": against})
+        whole = b.prove_prefix("users/", addressing=addressing)
+        try:
+            verify_prefix({**whole, "nodes": whole["nodes"][1:]}, root_b)
+            raise AssertionError("Python accepted a prefix proof missing a node")
+        except ProofError:
+            refused.append({"kind": "prefix", "name": "a node missing",
+                            "envelope": {**whole, "nodes": whole["nodes"][1:]}, "root": root_b})
+        ext = b.prove_extension(root_a, ["log/"], addressing=addressing)
+        try:
+            verify_extension({**ext, "prefixes": [""]}, root_a, root_b)
+            raise AssertionError("Python accepted an extension proof for a wider prefix")
+        except ProofError:
+            refused.append({"kind": "extension", "name": "claims a wider prefix",
+                            "envelope": {**ext, "prefixes": [""]}, "base": root_a, "root": root_b})
+
+        cases.append({
+            "addressing": addressing,
+            "first": [_op_json(op) for op in first],
+            "second": [_op_json(op) for op in second],
+            "roots": {"a": root_a, "b": root_b},
+            "empty": empty,
+            "keys": [key_proof(s, k) for s in (a, b) for k in keys],
+            "prefixes": [prefix_proof(s, p) for s in (a, b) for p in prefixes],
+            "extensions": extensions,
+            "refused": refused,
+        })
+    return cases
+
+
+# ---------------------------------------------------------------------------
+
+def fixtures():
+    note = ("Generated by js/tools/make_fixtures.py from the Python "
+            "reference implementation; do not edit by hand.")
+    return {
+        "canonical.json": {"note": note, "values": canonical_cases(),
+                           "records": value_blob_cases()},
+        "swarm-addresses.json": {"note": note,
+                                 "data": "byte i = (i * 31 + 7) & 0xff",
+                                 "cases": swarm_cases()},
+        "stores.json": {"note": note, "cases": store_cases()},
+        "merges.json": {"note": note, "resolver": "ours wins: DELETE if ours "
+                        "is ABSENT, else ours", "cases": merge_cases()},
+        "feeds.json": {"note": note, **feed_cases()},
+        "proofs.json": {"note": note, "cases": proof_cases()},
+    }
+
+
+def render(obj) -> str:
+    return json.dumps(obj, ensure_ascii=False, indent=1, allow_nan=False) + "\n"
+
+
+def main(argv):
+    check = "--check" in argv
+    stale = []
+    os.makedirs(OUT, exist_ok=True)
+    for name, obj in fixtures().items():
+        path = os.path.join(OUT, name)
+        text = render(obj)
+        if check:
+            try:
+                with open(path, encoding="utf-8") as f:
+                    if f.read() != text:
+                        stale.append(name)
+            except FileNotFoundError:
+                stale.append(name)
+        else:
+            with open(path, "w", encoding="utf-8") as f:
+                f.write(text)
+            print(f"wrote {os.path.relpath(path, REPO)}")
+    if stale:
+        print("stale fixtures (rerun js/tools/make_fixtures.py): "
+              + ", ".join(stale), file=sys.stderr)
+        return 1
+    if check:
+        print("fixtures are current")
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))
diff --git a/js/vendor/js-sha3/LICENSE b/js/vendor/js-sha3/LICENSE
new file mode 100644
index 0000000..c1ee2fb
--- /dev/null
+++ b/js/vendor/js-sha3/LICENSE
@@ -0,0 +1,20 @@
+Copyright 2015-2023 Chen, Yi-Cyuan
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/js/vendor/js-sha3/PROVENANCE.md b/js/vendor/js-sha3/PROVENANCE.md
new file mode 100644
index 0000000..7e90812
--- /dev/null
+++ b/js/vendor/js-sha3/PROVENANCE.md
@@ -0,0 +1,23 @@
+# Vendored: js-sha3 0.9.3
+
+Source: the `js-sha3` npm package, version 0.9.3
+(https://github.com/emn178/js-sha3), MIT license (see LICENSE).
+
+Vendored subset: `src/sha3.js` only (unmodified), plus the `index.js` ESM
+facade. Copied from swarmlite's `js/vendor/js-sha3/`, which vendored it
+first; same file, same version.
+
+SHA-256 of `sha3.js`:
+`6378cc474ad0a0db3e88c0c67b7bd4a154e1b318f44dbbd13927f6e220efaa38`
+
+Used for keccak256 in Swarm (BMT) content addressing (`src/address.js`).
+
+Vendored (rather than npm-installed) so that a page published on Swarm can
+import recordstore with relative URLs only: no registry, no CDN, no build.
+
+Sanity check: `keccak256('hello')` =
+`1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8`
+(the well-known Ethereum keccak vector; asserted in `test/address.test.js`).
+
+To upgrade: `npm pack js-sha3`, extract, copy `src/sha3.js` and LICENSE,
+update the version and hash here, run `npm test`.
diff --git a/js/vendor/js-sha3/index.js b/js/vendor/js-sha3/index.js
new file mode 100644
index 0000000..483fe8e
--- /dev/null
+++ b/js/vendor/js-sha3/index.js
@@ -0,0 +1,12 @@
+// ESM facade over the unmodified UMD build: in ES-module scope neither
+// `module` nor `define` exists, so sha3.js falls through to attaching
+// its methods to the global object (window / self / global); re-export
+// the one method swarmlite uses.
+import './sha3.js';
+
+/** keccak256 over a string or byte array -> lowercase hex (no 0x). */
+export const keccak256 = globalThis.keccak_256;
+
+/** keccak256 over a byte array -> Uint8Array (for BMT/SOC hashing). */
+export const keccak256Bytes = (data) =>
+  Uint8Array.from(globalThis.keccak_256.array(data));
diff --git a/js/vendor/js-sha3/sha3.js b/js/vendor/js-sha3/sha3.js
new file mode 100644
index 0000000..52c9334
--- /dev/null
+++ b/js/vendor/js-sha3/sha3.js
@@ -0,0 +1,662 @@
+/**
+ * [js-sha3]{@link https://github.com/emn178/js-sha3}
+ *
+ * @version 0.9.3
+ * @author Chen, Yi-Cyuan [emn178@gmail.com]
+ * @copyright Chen, Yi-Cyuan 2015-2023
+ * @license MIT
+ */
+/*jslint bitwise: true */
+(function () {
+  'use strict';
+
+  var INPUT_ERROR = 'input is invalid type';
+  var FINALIZE_ERROR = 'finalize already called';
+  var WINDOW = typeof window === 'object';
+  var root = WINDOW ? window : {};
+  if (root.JS_SHA3_NO_WINDOW) {
+    WINDOW = false;
+  }
+  var WEB_WORKER = !WINDOW && typeof self === 'object';
+  var NODE_JS = !root.JS_SHA3_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
+  if (NODE_JS) {
+    root = global;
+  } else if (WEB_WORKER) {
+    root = self;
+  }
+  var COMMON_JS = !root.JS_SHA3_NO_COMMON_JS && typeof module === 'object' && module.exports;
+  var AMD = typeof define === 'function' && define.amd;
+  var ARRAY_BUFFER = !root.JS_SHA3_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
+  var HEX_CHARS = '0123456789abcdef'.split('');
+  var SHAKE_PADDING = [31, 7936, 2031616, 520093696];
+  var CSHAKE_PADDING = [4, 1024, 262144, 67108864];
+  var KECCAK_PADDING = [1, 256, 65536, 16777216];
+  var PADDING = [6, 1536, 393216, 100663296];
+  var SHIFT = [0, 8, 16, 24];
+  var RC = [1, 0, 32898, 0, 32906, 2147483648, 2147516416, 2147483648, 32907, 0, 2147483649,
+    0, 2147516545, 2147483648, 32777, 2147483648, 138, 0, 136, 0, 2147516425, 0,
+    2147483658, 0, 2147516555, 0, 139, 2147483648, 32905, 2147483648, 32771,
+    2147483648, 32770, 2147483648, 128, 2147483648, 32778, 0, 2147483658, 2147483648,
+    2147516545, 2147483648, 32896, 2147483648, 2147483649, 0, 2147516424, 2147483648];
+  var BITS = [224, 256, 384, 512];
+  var SHAKE_BITS = [128, 256];
+  var OUTPUT_TYPES = ['hex', 'buffer', 'arrayBuffer', 'array', 'digest'];
+  var CSHAKE_BYTEPAD = {
+    '128': 168,
+    '256': 136
+  };
+
+
+  var isArray = root.JS_SHA3_NO_NODE_JS || !Array.isArray
+    ? function (obj) {
+        return Object.prototype.toString.call(obj) === '[object Array]';
+      }
+    : Array.isArray;
+
+  var isView = (ARRAY_BUFFER && (root.JS_SHA3_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView))
+    ? function (obj) {
+        return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
+      }
+    : ArrayBuffer.isView;
+
+  // [message: string, isString: bool]
+  var formatMessage = function (message) {
+    var type = typeof message;
+    if (type === 'string') {
+      return [message, true];
+    }
+    if (type !== 'object' || message === null) {
+      throw new Error(INPUT_ERROR);
+    }
+    if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
+      return [new Uint8Array(message), false];
+    }
+    if (!isArray(message) && !isView(message)) {
+      throw new Error(INPUT_ERROR);
+    }
+    return [message, false];
+  }
+
+  var empty = function (message) {
+    return formatMessage(message)[0].length === 0;
+  };
+
+  var cloneArray = function (array) {
+    var newArray = [];
+    for (var i = 0; i < array.length; ++i) {
+      newArray[i] = array[i];
+    }
+    return newArray;
+  }
+
+  var createOutputMethod = function (bits, padding, outputType) {
+    return function (message) {
+      return new Keccak(bits, padding, bits).update(message)[outputType]();
+    };
+  };
+
+  var createShakeOutputMethod = function (bits, padding, outputType) {
+    return function (message, outputBits) {
+      return new Keccak(bits, padding, outputBits).update(message)[outputType]();
+    };
+  };
+
+  var createCshakeOutputMethod = function (bits, padding, outputType) {
+    return function (message, outputBits, n, s) {
+      return methods['cshake' + bits].update(message, outputBits, n, s)[outputType]();
+    };
+  };
+
+  var createKmacOutputMethod = function (bits, padding, outputType) {
+    return function (key, message, outputBits, s) {
+      return methods['kmac' + bits].update(key, message, outputBits, s)[outputType]();
+    };
+  };
+
+  var createOutputMethods = function (method, createMethod, bits, padding) {
+    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
+      var type = OUTPUT_TYPES[i];
+      method[type] = createMethod(bits, padding, type);
+    }
+    return method;
+  };
+
+  var createMethod = function (bits, padding) {
+    var method = createOutputMethod(bits, padding, 'hex');
+    method.create = function () {
+      return new Keccak(bits, padding, bits);
+    };
+    method.update = function (message) {
+      return method.create().update(message);
+    };
+    return createOutputMethods(method, createOutputMethod, bits, padding);
+  };
+
+  var createShakeMethod = function (bits, padding) {
+    var method = createShakeOutputMethod(bits, padding, 'hex');
+    method.create = function (outputBits) {
+      return new Keccak(bits, padding, outputBits);
+    };
+    method.update = function (message, outputBits) {
+      return method.create(outputBits).update(message);
+    };
+    return createOutputMethods(method, createShakeOutputMethod, bits, padding);
+  };
+
+  var createCshakeMethod = function (bits, padding) {
+    var w = CSHAKE_BYTEPAD[bits];
+    var method = createCshakeOutputMethod(bits, padding, 'hex');
+    method.create = function (outputBits, n, s) {
+      if (empty(n) && empty(s)) {
+        return methods['shake' + bits].create(outputBits);
+      } else {
+        return new Keccak(bits, padding, outputBits).bytepad([n, s], w);
+      }
+    };
+    method.update = function (message, outputBits, n, s) {
+      return method.create(outputBits, n, s).update(message);
+    };
+    return createOutputMethods(method, createCshakeOutputMethod, bits, padding);
+  };
+
+  var createKmacMethod = function (bits, padding) {
+    var w = CSHAKE_BYTEPAD[bits];
+    var method = createKmacOutputMethod(bits, padding, 'hex');
+    method.create = function (key, outputBits, s) {
+      return new Kmac(bits, padding, outputBits).bytepad(['KMAC', s], w).bytepad([key], w);
+    };
+    method.update = function (key, message, outputBits, s) {
+      return method.create(key, outputBits, s).update(message);
+    };
+    return createOutputMethods(method, createKmacOutputMethod, bits, padding);
+  };
+
+  var algorithms = [
+    { name: 'keccak', padding: KECCAK_PADDING, bits: BITS, createMethod: createMethod },
+    { name: 'sha3', padding: PADDING, bits: BITS, createMethod: createMethod },
+    { name: 'shake', padding: SHAKE_PADDING, bits: SHAKE_BITS, createMethod: createShakeMethod },
+    { name: 'cshake', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createCshakeMethod },
+    { name: 'kmac', padding: CSHAKE_PADDING, bits: SHAKE_BITS, createMethod: createKmacMethod }
+  ];
+
+  var methods = {}, methodNames = [];
+
+  for (var i = 0; i < algorithms.length; ++i) {
+    var algorithm = algorithms[i];
+    var bits = algorithm.bits;
+    for (var j = 0; j < bits.length; ++j) {
+      var methodName = algorithm.name + '_' + bits[j];
+      methodNames.push(methodName);
+      methods[methodName] = algorithm.createMethod(bits[j], algorithm.padding);
+      if (algorithm.name !== 'sha3') {
+        var newMethodName = algorithm.name + bits[j];
+        methodNames.push(newMethodName);
+        methods[newMethodName] = methods[methodName];
+      }
+    }
+  }
+
+  function Keccak(bits, padding, outputBits) {
+    this.blocks = [];
+    this.s = [];
+    this.padding = padding;
+    this.outputBits = outputBits;
+    this.reset = true;
+    this.finalized = false;
+    this.block = 0;
+    this.start = 0;
+    this.blockCount = (1600 - (bits << 1)) >> 5;
+    this.byteCount = this.blockCount << 2;
+    this.outputBlocks = outputBits >> 5;
+    this.extraBytes = (outputBits & 31) >> 3;
+
+    for (var i = 0; i < 50; ++i) {
+      this.s[i] = 0;
+    }
+  }
+
+  Keccak.prototype.update = function (message) {
+    if (this.finalized) {
+      throw new Error(FINALIZE_ERROR);
+    }
+    var result = formatMessage(message);
+    message = result[0];
+    var isString = result[1];
+    var blocks = this.blocks, byteCount = this.byteCount, length = message.length,
+      blockCount = this.blockCount, index = 0, s = this.s, i, code;
+
+    while (index < length) {
+      if (this.reset) {
+        this.reset = false;
+        blocks[0] = this.block;
+        for (i = 1; i < blockCount + 1; ++i) {
+          blocks[i] = 0;
+        }
+      }
+      if (isString) {
+        for (i = this.start; index < length && i < byteCount; ++index) {
+          code = message.charCodeAt(index);
+          if (code < 0x80) {
+            blocks[i >> 2] |= code << SHIFT[i++ & 3];
+          } else if (code < 0x800) {
+            blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+          } else if (code < 0xd800 || code >= 0xe000) {
+            blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+          } else {
+            code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
+            blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
+            blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
+          }
+        }
+      } else {
+        for (i = this.start; index < length && i < byteCount; ++index) {
+          blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
+        }
+      }
+      this.lastByteIndex = i;
+      if (i >= byteCount) {
+        this.start = i - byteCount;
+        this.block = blocks[blockCount];
+        for (i = 0; i < blockCount; ++i) {
+          s[i] ^= blocks[i];
+        }
+        f(s);
+        this.reset = true;
+      } else {
+        this.start = i;
+      }
+    }
+    return this;
+  };
+
+  Keccak.prototype.encode = function (x, right) {
+    var o = x & 255, n = 1;
+    var bytes = [o];
+    x = x >> 8;
+    o = x & 255;
+    while (o > 0) {
+      bytes.unshift(o);
+      x = x >> 8;
+      o = x & 255;
+      ++n;
+    }
+    if (right) {
+      bytes.push(n);
+    } else {
+      bytes.unshift(n);
+    }
+    this.update(bytes);
+    return bytes.length;
+  };
+
+  Keccak.prototype.encodeString = function (str) {
+    var result = formatMessage(str);
+    str = result[0];
+    var isString = result[1];
+    var bytes = 0, length = str.length;
+    if (isString) {
+      for (var i = 0; i < str.length; ++i) {
+        var code = str.charCodeAt(i);
+        if (code < 0x80) {
+          bytes += 1;
+        } else if (code < 0x800) {
+          bytes += 2;
+        } else if (code < 0xd800 || code >= 0xe000) {
+          bytes += 3;
+        } else {
+          code = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(++i) & 0x3ff));
+          bytes += 4;
+        }
+      }
+    } else {
+      bytes = length;
+    }
+    bytes += this.encode(bytes * 8);
+    this.update(str);
+    return bytes;
+  };
+
+  Keccak.prototype.bytepad = function (strs, w) {
+    var bytes = this.encode(w);
+    for (var i = 0; i < strs.length; ++i) {
+      bytes += this.encodeString(strs[i]);
+    }
+    var paddingBytes = (w - bytes % w) % w;
+    var zeros = [];
+    zeros.length = paddingBytes;
+    this.update(zeros);
+    return this;
+  };
+
+  Keccak.prototype.finalize = function () {
+    if (this.finalized) {
+      return;
+    }
+    this.finalized = true;
+    var blocks = this.blocks, i = this.lastByteIndex, blockCount = this.blockCount, s = this.s;
+    blocks[i >> 2] |= this.padding[i & 3];
+    if (this.lastByteIndex === this.byteCount) {
+      blocks[0] = blocks[blockCount];
+      for (i = 1; i < blockCount + 1; ++i) {
+        blocks[i] = 0;
+      }
+    }
+    blocks[blockCount - 1] |= 0x80000000;
+    for (i = 0; i < blockCount; ++i) {
+      s[i] ^= blocks[i];
+    }
+    f(s);
+  };
+
+  Keccak.prototype.toString = Keccak.prototype.hex = function () {
+    this.finalize();
+
+    var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
+      extraBytes = this.extraBytes, i = 0, j = 0;
+    var hex = '', block;
+    while (j < outputBlocks) {
+      for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
+        block = s[i];
+        hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F] +
+          HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F] +
+          HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F] +
+          HEX_CHARS[(block >> 28) & 0x0F] + HEX_CHARS[(block >> 24) & 0x0F];
+      }
+      if (j % blockCount === 0) {
+        s = cloneArray(s);
+        f(s);
+        i = 0;
+      }
+    }
+    if (extraBytes) {
+      block = s[i];
+      hex += HEX_CHARS[(block >> 4) & 0x0F] + HEX_CHARS[block & 0x0F];
+      if (extraBytes > 1) {
+        hex += HEX_CHARS[(block >> 12) & 0x0F] + HEX_CHARS[(block >> 8) & 0x0F];
+      }
+      if (extraBytes > 2) {
+        hex += HEX_CHARS[(block >> 20) & 0x0F] + HEX_CHARS[(block >> 16) & 0x0F];
+      }
+    }
+    return hex;
+  };
+
+  Keccak.prototype.arrayBuffer = function () {
+    this.finalize();
+
+    var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
+      extraBytes = this.extraBytes, i = 0, j = 0;
+    var bytes = this.outputBits >> 3;
+    var buffer;
+    if (extraBytes) {
+      buffer = new ArrayBuffer((outputBlocks + 1) << 2);
+    } else {
+      buffer = new ArrayBuffer(bytes);
+    }
+    var array = new Uint32Array(buffer);
+    while (j < outputBlocks) {
+      for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
+        array[j] = s[i];
+      }
+      if (j % blockCount === 0) {
+        s = cloneArray(s);
+        f(s);
+      }
+    }
+    if (extraBytes) {
+      array[j] = s[i];
+      buffer = buffer.slice(0, bytes);
+    }
+    return buffer;
+  };
+
+  Keccak.prototype.buffer = Keccak.prototype.arrayBuffer;
+
+  Keccak.prototype.digest = Keccak.prototype.array = function () {
+    this.finalize();
+
+    var blockCount = this.blockCount, s = this.s, outputBlocks = this.outputBlocks,
+      extraBytes = this.extraBytes, i = 0, j = 0;
+    var array = [], offset, block;
+    while (j < outputBlocks) {
+      for (i = 0; i < blockCount && j < outputBlocks; ++i, ++j) {
+        offset = j << 2;
+        block = s[i];
+        array[offset] = block & 0xFF;
+        array[offset + 1] = (block >> 8) & 0xFF;
+        array[offset + 2] = (block >> 16) & 0xFF;
+        array[offset + 3] = (block >> 24) & 0xFF;
+      }
+      if (j % blockCount === 0) {
+        s = cloneArray(s);
+        f(s);
+      }
+    }
+    if (extraBytes) {
+      offset = j << 2;
+      block = s[i];
+      array[offset] = block & 0xFF;
+      if (extraBytes > 1) {
+        array[offset + 1] = (block >> 8) & 0xFF;
+      }
+      if (extraBytes > 2) {
+        array[offset + 2] = (block >> 16) & 0xFF;
+      }
+    }
+    return array;
+  };
+
+  function Kmac(bits, padding, outputBits) {
+    Keccak.call(this, bits, padding, outputBits);
+  }
+
+  Kmac.prototype = new Keccak();
+
+  Kmac.prototype.finalize = function () {
+    this.encode(this.outputBits, true);
+    return Keccak.prototype.finalize.call(this);
+  };
+
+  var f = function (s) {
+    var h, l, n, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9,
+      b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17,
+      b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31, b32, b33,
+      b34, b35, b36, b37, b38, b39, b40, b41, b42, b43, b44, b45, b46, b47, b48, b49;
+    for (n = 0; n < 48; n += 2) {
+      c0 = s[0] ^ s[10] ^ s[20] ^ s[30] ^ s[40];
+      c1 = s[1] ^ s[11] ^ s[21] ^ s[31] ^ s[41];
+      c2 = s[2] ^ s[12] ^ s[22] ^ s[32] ^ s[42];
+      c3 = s[3] ^ s[13] ^ s[23] ^ s[33] ^ s[43];
+      c4 = s[4] ^ s[14] ^ s[24] ^ s[34] ^ s[44];
+      c5 = s[5] ^ s[15] ^ s[25] ^ s[35] ^ s[45];
+      c6 = s[6] ^ s[16] ^ s[26] ^ s[36] ^ s[46];
+      c7 = s[7] ^ s[17] ^ s[27] ^ s[37] ^ s[47];
+      c8 = s[8] ^ s[18] ^ s[28] ^ s[38] ^ s[48];
+      c9 = s[9] ^ s[19] ^ s[29] ^ s[39] ^ s[49];
+
+      h = c8 ^ ((c2 << 1) | (c3 >>> 31));
+      l = c9 ^ ((c3 << 1) | (c2 >>> 31));
+      s[0] ^= h;
+      s[1] ^= l;
+      s[10] ^= h;
+      s[11] ^= l;
+      s[20] ^= h;
+      s[21] ^= l;
+      s[30] ^= h;
+      s[31] ^= l;
+      s[40] ^= h;
+      s[41] ^= l;
+      h = c0 ^ ((c4 << 1) | (c5 >>> 31));
+      l = c1 ^ ((c5 << 1) | (c4 >>> 31));
+      s[2] ^= h;
+      s[3] ^= l;
+      s[12] ^= h;
+      s[13] ^= l;
+      s[22] ^= h;
+      s[23] ^= l;
+      s[32] ^= h;
+      s[33] ^= l;
+      s[42] ^= h;
+      s[43] ^= l;
+      h = c2 ^ ((c6 << 1) | (c7 >>> 31));
+      l = c3 ^ ((c7 << 1) | (c6 >>> 31));
+      s[4] ^= h;
+      s[5] ^= l;
+      s[14] ^= h;
+      s[15] ^= l;
+      s[24] ^= h;
+      s[25] ^= l;
+      s[34] ^= h;
+      s[35] ^= l;
+      s[44] ^= h;
+      s[45] ^= l;
+      h = c4 ^ ((c8 << 1) | (c9 >>> 31));
+      l = c5 ^ ((c9 << 1) | (c8 >>> 31));
+      s[6] ^= h;
+      s[7] ^= l;
+      s[16] ^= h;
+      s[17] ^= l;
+      s[26] ^= h;
+      s[27] ^= l;
+      s[36] ^= h;
+      s[37] ^= l;
+      s[46] ^= h;
+      s[47] ^= l;
+      h = c6 ^ ((c0 << 1) | (c1 >>> 31));
+      l = c7 ^ ((c1 << 1) | (c0 >>> 31));
+      s[8] ^= h;
+      s[9] ^= l;
+      s[18] ^= h;
+      s[19] ^= l;
+      s[28] ^= h;
+      s[29] ^= l;
+      s[38] ^= h;
+      s[39] ^= l;
+      s[48] ^= h;
+      s[49] ^= l;
+
+      b0 = s[0];
+      b1 = s[1];
+      b32 = (s[11] << 4) | (s[10] >>> 28);
+      b33 = (s[10] << 4) | (s[11] >>> 28);
+      b14 = (s[20] << 3) | (s[21] >>> 29);
+      b15 = (s[21] << 3) | (s[20] >>> 29);
+      b46 = (s[31] << 9) | (s[30] >>> 23);
+      b47 = (s[30] << 9) | (s[31] >>> 23);
+      b28 = (s[40] << 18) | (s[41] >>> 14);
+      b29 = (s[41] << 18) | (s[40] >>> 14);
+      b20 = (s[2] << 1) | (s[3] >>> 31);
+      b21 = (s[3] << 1) | (s[2] >>> 31);
+      b2 = (s[13] << 12) | (s[12] >>> 20);
+      b3 = (s[12] << 12) | (s[13] >>> 20);
+      b34 = (s[22] << 10) | (s[23] >>> 22);
+      b35 = (s[23] << 10) | (s[22] >>> 22);
+      b16 = (s[33] << 13) | (s[32] >>> 19);
+      b17 = (s[32] << 13) | (s[33] >>> 19);
+      b48 = (s[42] << 2) | (s[43] >>> 30);
+      b49 = (s[43] << 2) | (s[42] >>> 30);
+      b40 = (s[5] << 30) | (s[4] >>> 2);
+      b41 = (s[4] << 30) | (s[5] >>> 2);
+      b22 = (s[14] << 6) | (s[15] >>> 26);
+      b23 = (s[15] << 6) | (s[14] >>> 26);
+      b4 = (s[25] << 11) | (s[24] >>> 21);
+      b5 = (s[24] << 11) | (s[25] >>> 21);
+      b36 = (s[34] << 15) | (s[35] >>> 17);
+      b37 = (s[35] << 15) | (s[34] >>> 17);
+      b18 = (s[45] << 29) | (s[44] >>> 3);
+      b19 = (s[44] << 29) | (s[45] >>> 3);
+      b10 = (s[6] << 28) | (s[7] >>> 4);
+      b11 = (s[7] << 28) | (s[6] >>> 4);
+      b42 = (s[17] << 23) | (s[16] >>> 9);
+      b43 = (s[16] << 23) | (s[17] >>> 9);
+      b24 = (s[26] << 25) | (s[27] >>> 7);
+      b25 = (s[27] << 25) | (s[26] >>> 7);
+      b6 = (s[36] << 21) | (s[37] >>> 11);
+      b7 = (s[37] << 21) | (s[36] >>> 11);
+      b38 = (s[47] << 24) | (s[46] >>> 8);
+      b39 = (s[46] << 24) | (s[47] >>> 8);
+      b30 = (s[8] << 27) | (s[9] >>> 5);
+      b31 = (s[9] << 27) | (s[8] >>> 5);
+      b12 = (s[18] << 20) | (s[19] >>> 12);
+      b13 = (s[19] << 20) | (s[18] >>> 12);
+      b44 = (s[29] << 7) | (s[28] >>> 25);
+      b45 = (s[28] << 7) | (s[29] >>> 25);
+      b26 = (s[38] << 8) | (s[39] >>> 24);
+      b27 = (s[39] << 8) | (s[38] >>> 24);
+      b8 = (s[48] << 14) | (s[49] >>> 18);
+      b9 = (s[49] << 14) | (s[48] >>> 18);
+
+      s[0] = b0 ^ (~b2 & b4);
+      s[1] = b1 ^ (~b3 & b5);
+      s[10] = b10 ^ (~b12 & b14);
+      s[11] = b11 ^ (~b13 & b15);
+      s[20] = b20 ^ (~b22 & b24);
+      s[21] = b21 ^ (~b23 & b25);
+      s[30] = b30 ^ (~b32 & b34);
+      s[31] = b31 ^ (~b33 & b35);
+      s[40] = b40 ^ (~b42 & b44);
+      s[41] = b41 ^ (~b43 & b45);
+      s[2] = b2 ^ (~b4 & b6);
+      s[3] = b3 ^ (~b5 & b7);
+      s[12] = b12 ^ (~b14 & b16);
+      s[13] = b13 ^ (~b15 & b17);
+      s[22] = b22 ^ (~b24 & b26);
+      s[23] = b23 ^ (~b25 & b27);
+      s[32] = b32 ^ (~b34 & b36);
+      s[33] = b33 ^ (~b35 & b37);
+      s[42] = b42 ^ (~b44 & b46);
+      s[43] = b43 ^ (~b45 & b47);
+      s[4] = b4 ^ (~b6 & b8);
+      s[5] = b5 ^ (~b7 & b9);
+      s[14] = b14 ^ (~b16 & b18);
+      s[15] = b15 ^ (~b17 & b19);
+      s[24] = b24 ^ (~b26 & b28);
+      s[25] = b25 ^ (~b27 & b29);
+      s[34] = b34 ^ (~b36 & b38);
+      s[35] = b35 ^ (~b37 & b39);
+      s[44] = b44 ^ (~b46 & b48);
+      s[45] = b45 ^ (~b47 & b49);
+      s[6] = b6 ^ (~b8 & b0);
+      s[7] = b7 ^ (~b9 & b1);
+      s[16] = b16 ^ (~b18 & b10);
+      s[17] = b17 ^ (~b19 & b11);
+      s[26] = b26 ^ (~b28 & b20);
+      s[27] = b27 ^ (~b29 & b21);
+      s[36] = b36 ^ (~b38 & b30);
+      s[37] = b37 ^ (~b39 & b31);
+      s[46] = b46 ^ (~b48 & b40);
+      s[47] = b47 ^ (~b49 & b41);
+      s[8] = b8 ^ (~b0 & b2);
+      s[9] = b9 ^ (~b1 & b3);
+      s[18] = b18 ^ (~b10 & b12);
+      s[19] = b19 ^ (~b11 & b13);
+      s[28] = b28 ^ (~b20 & b22);
+      s[29] = b29 ^ (~b21 & b23);
+      s[38] = b38 ^ (~b30 & b32);
+      s[39] = b39 ^ (~b31 & b33);
+      s[48] = b48 ^ (~b40 & b42);
+      s[49] = b49 ^ (~b41 & b43);
+
+      s[0] ^= RC[n];
+      s[1] ^= RC[n + 1];
+    }
+  };
+
+  if (COMMON_JS) {
+    module.exports = methods;
+  } else {
+    for (i = 0; i < methodNames.length; ++i) {
+      root[methodNames[i]] = methods[methodNames[i]];
+    }
+    if (AMD) {
+      define(function () {
+        return methods;
+      });
+    }
+  }
+})();
diff --git a/js/vendor/noble-secp256k1/LICENSE b/js/vendor/noble-secp256k1/LICENSE
new file mode 100644
index 0000000..a5e0151
--- /dev/null
+++ b/js/vendor/noble-secp256k1/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2019 Paul Miller (https://paulmillr.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the “Software”), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/js/vendor/noble-secp256k1/PROVENANCE.md b/js/vendor/noble-secp256k1/PROVENANCE.md
new file mode 100644
index 0000000..cfb988f
--- /dev/null
+++ b/js/vendor/noble-secp256k1/PROVENANCE.md
@@ -0,0 +1,24 @@
+# Vendored: @noble/secp256k1 2.3.0
+
+Source: the `@noble/secp256k1` npm package, version 2.3.0
+(https://github.com/paulmillr/noble-secp256k1), MIT license (see LICENSE).
+Single-file, zero-dependency, audited ECDSA implementation.
+
+Vendored subset: `index.js` only (unmodified ESM). Copied from swarmlite's
+`js/vendor/noble-secp256k1/`, which vendored it first; same file, same
+version.
+
+SHA-256 of `index.js`:
+`8ba6138826758ef7d13243f18f7fbf8b26c81c6abea706e692baa83dfe3827e1`
+
+Used for feed updates (`src/feeds.js`): signing them (`signAsync`, RFC 6979
+deterministic with low S, the same signatures libsecp256k1 makes for
+Python) and recovering the signer of each one read, so a forged update is
+caught even through an untrusted node or gateway.
+
+Vendored (rather than npm-installed) so that a page published on Swarm can
+import recordstore with relative URLs only.
+
+To upgrade: `npm pack @noble/secp256k1`, extract, copy `index.js` and
+LICENSE, update the version and hash here, run `npm test` (the feed
+fixtures pin signatures byte for byte).
diff --git a/js/vendor/noble-secp256k1/index.js b/js/vendor/noble-secp256k1/index.js
new file mode 100644
index 0000000..187f25d
--- /dev/null
+++ b/js/vendor/noble-secp256k1/index.js
@@ -0,0 +1,790 @@
+/*! noble-secp256k1 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
+/**
+ * 4KB JS implementation of secp256k1 ECDSA / Schnorr signatures & ECDH.
+ * Compliant with RFC6979 & BIP340.
+ * @module
+ */
+/**
+ * Curve params. secp256k1 is short weierstrass / koblitz curve. Equation is y² == x³ + ax + b.
+ * * P = `2n**256n-2n**32n-2n**977n` // field over which calculations are done
+ * * N = `2n**256n - 0x14551231950b75fc4402da1732fc9bebfn` // group order, amount of curve points
+ * * h = `1n` // cofactor
+ * * a = `0n` // equation param
+ * * b = `7n` // equation param
+ * * Gx, Gy are coordinates of Generator / base point
+ */
+const secp256k1_CURVE = {
+    p: 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2fn,
+    n: 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n,
+    h: 1n,
+    a: 0n,
+    b: 7n,
+    Gx: 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798n,
+    Gy: 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8n,
+};
+const { p: P, n: N, Gx, Gy, b: _b } = secp256k1_CURVE;
+const L = 32; // field / group byte length
+const L2 = 64;
+// Helpers and Precomputes sections are reused between libraries
+// ## Helpers
+// ----------
+// error helper, messes-up stack trace
+const err = (m = '') => {
+    throw new Error(m);
+};
+const isBig = (n) => typeof n === 'bigint'; // is big integer
+const isStr = (s) => typeof s === 'string'; // is string
+const isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
+/** assert is Uint8Array (of specific length) */
+const abytes = (a, l) => !isBytes(a) || (typeof l === 'number' && l > 0 && a.length !== l)
+    ? err('Uint8Array expected')
+    : a;
+/** create Uint8Array */
+const u8n = (len) => new Uint8Array(len);
+const u8fr = (buf) => Uint8Array.from(buf);
+const padh = (n, pad) => n.toString(16).padStart(pad, '0');
+const bytesToHex = (b) => Array.from(abytes(b))
+    .map((e) => padh(e, 2))
+    .join('');
+const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
+const _ch = (ch) => {
+    if (ch >= C._0 && ch <= C._9)
+        return ch - C._0; // '2' => 50-48
+    if (ch >= C.A && ch <= C.F)
+        return ch - (C.A - 10); // 'B' => 66-(65-10)
+    if (ch >= C.a && ch <= C.f)
+        return ch - (C.a - 10); // 'b' => 98-(97-10)
+    return;
+};
+const hexToBytes = (hex) => {
+    const e = 'hex invalid';
+    if (!isStr(hex))
+        return err(e);
+    const hl = hex.length;
+    const al = hl / 2;
+    if (hl % 2)
+        return err(e);
+    const array = u8n(al);
+    for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
+        // treat each char as ASCII
+        const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
+        const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
+        if (n1 === undefined || n2 === undefined)
+            return err(e);
+        array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
+    }
+    return array;
+};
+/** normalize hex or ui8a to ui8a */
+const toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
+const cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments
+const subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined');
+// prettier-ignore
+const concatBytes = (...arrs) => {
+    const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length
+    let pad = 0; // walk through each array,
+    arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
+    return r;
+};
+/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */
+const randomBytes = (len = L) => {
+    const c = cr();
+    return c.getRandomValues(u8n(len));
+};
+const big = BigInt;
+const arange = (n, min, max, msg = 'bad number: out of range') => isBig(n) && min <= n && n < max ? n : err(msg);
+/** modular division */
+const M = (a, b = P) => {
+    const r = a % b;
+    return r >= 0n ? r : b + r;
+};
+const modN = (a) => M(a, N);
+/** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
+// prettier-ignore
+const invert = (num, md) => {
+    if (num === 0n || md <= 0n)
+        err('no inverse n=' + num + ' mod=' + md);
+    let a = M(num, md), b = md, x = 0n, y = 1n, u = 1n, v = 0n;
+    while (a !== 0n) {
+        const q = b / a, r = b % a;
+        const m = x - u * q, n = y - v * q;
+        b = a, a = r, x = u, y = v, u = m, v = n;
+    }
+    return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
+};
+const callHash = (name) => {
+    // @ts-ignore
+    const fn = etc[name];
+    if (typeof fn !== 'function')
+        err('hashes.' + name + ' not set');
+    return fn;
+};
+const apoint = (p) => (p instanceof Point ? p : err('Point expected'));
+// ## End of Helpers
+// -----------------
+/** secp256k1 formula. Koblitz curves are subclass of weierstrass curves with a=0, making it x³+b */
+const koblitz = (x) => M(M(x * x) * x + _b);
+/** assert is field element or 0 */
+const afield0 = (n) => arange(n, 0n, P);
+/** assert is field element */
+const afield = (n) => arange(n, 1n, P);
+/** assert is group elem */
+const agroup = (n) => arange(n, 1n, N);
+const isEven = (y) => (y & 1n) === 0n;
+/** create Uint8Array of byte n */
+const u8of = (n) => Uint8Array.of(n);
+const getPrefix = (y) => u8of(isEven(y) ? 0x02 : 0x03);
+/** lift_x from BIP340 calculates square root. Validates x, then validates root*root. */
+const lift_x = (x) => {
+    // Let c = x³ + 7 mod p. Fail if x ≥ p. (also fail if x < 1)
+    const c = koblitz(afield(x));
+    // c = √y
+    // y = c^((p+1)/4) mod p
+    // This formula works for fields p = 3 mod 4 -- a special, fast case.
+    // Paper: "Square Roots from 1;24,51,10 to Dan Shanks".
+    let r = 1n;
+    for (let num = c, e = (P + 1n) / 4n; e > 0n; e >>= 1n) {
+        // powMod: modular exponentiation.
+        if (e & 1n)
+            r = (r * num) % P; // Uses exponentiation by squaring.
+        num = (num * num) % P; // Not constant-time.
+    }
+    return M(r * r) === c ? r : err('sqrt invalid'); // check if result is valid
+};
+/** Point in 3d xyz projective coordinates. 3d takes less inversions than 2d. */
+class Point {
+    static BASE;
+    static ZERO;
+    px;
+    py;
+    pz;
+    constructor(px, py, pz) {
+        this.px = afield0(px);
+        this.py = afield(py); // y can't be 0 in Projective
+        this.pz = afield0(pz);
+        Object.freeze(this);
+    }
+    /** Convert Uint8Array or hex string to Point. */
+    static fromBytes(bytes) {
+        abytes(bytes);
+        let p = undefined;
+        // First byte is prefix, rest is data. There are 2 kinds: compressed & uncompressed:
+        // * [0x02 or 0x03][32-byte x coordinate]
+        // * [0x04]        [32-byte x coordinate][32-byte y coordinate]
+        const head = bytes[0];
+        const tail = bytes.subarray(1);
+        const x = sliceBytesNumBE(tail, 0, L);
+        const len = bytes.length;
+        // Compressed 33-byte point, 0x02 or 0x03 prefix
+        if (len === L + 1 && [0x02, 0x03].includes(head)) {
+            // Equation is y² == x³ + ax + b. We calculate y from x.
+            // y = √y²; there are two solutions: y, -y. Determine proper solution based on prefix
+            let y = lift_x(x);
+            const evenY = isEven(y);
+            const evenH = isEven(big(head));
+            if (evenH !== evenY)
+                y = M(-y);
+            p = new Point(x, y, 1n);
+        }
+        // Uncompressed 65-byte point, 0x04 prefix
+        if (len === L2 + 1 && head === 0x04)
+            p = new Point(x, sliceBytesNumBE(tail, L, L2), 1n);
+        // Validate point
+        return p ? p.assertValidity() : err('bad point: not on curve');
+    }
+    /** Equality check: compare points P&Q. */
+    equals(other) {
+        const { px: X1, py: Y1, pz: Z1 } = this;
+        const { px: X2, py: Y2, pz: Z2 } = apoint(other); // checks class equality
+        const X1Z2 = M(X1 * Z2);
+        const X2Z1 = M(X2 * Z1);
+        const Y1Z2 = M(Y1 * Z2);
+        const Y2Z1 = M(Y2 * Z1);
+        return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
+    }
+    is0() {
+        return this.equals(I);
+    }
+    /** Flip point over y coordinate. */
+    negate() {
+        return new Point(this.px, M(-this.py), this.pz);
+    }
+    /** Point doubling: P+P, complete formula. */
+    double() {
+        return this.add(this);
+    }
+    /**
+     * Point addition: P+Q, complete, exception-free formula
+     * (Renes-Costello-Batina, algo 1 of [2015/1060](https://eprint.iacr.org/2015/1060)).
+     * Cost: `12M + 0S + 3*a + 3*b3 + 23add`.
+     */
+    // prettier-ignore
+    add(other) {
+        const { px: X1, py: Y1, pz: Z1 } = this;
+        const { px: X2, py: Y2, pz: Z2 } = apoint(other);
+        const a = 0n;
+        const b = _b;
+        let X3 = 0n, Y3 = 0n, Z3 = 0n;
+        const b3 = M(b * 3n);
+        let t0 = M(X1 * X2), t1 = M(Y1 * Y2), t2 = M(Z1 * Z2), t3 = M(X1 + Y1); // step 1
+        let t4 = M(X2 + Y2); // step 5
+        t3 = M(t3 * t4);
+        t4 = M(t0 + t1);
+        t3 = M(t3 - t4);
+        t4 = M(X1 + Z1);
+        let t5 = M(X2 + Z2); // step 10
+        t4 = M(t4 * t5);
+        t5 = M(t0 + t2);
+        t4 = M(t4 - t5);
+        t5 = M(Y1 + Z1);
+        X3 = M(Y2 + Z2); // step 15
+        t5 = M(t5 * X3);
+        X3 = M(t1 + t2);
+        t5 = M(t5 - X3);
+        Z3 = M(a * t4);
+        X3 = M(b3 * t2); // step 20
+        Z3 = M(X3 + Z3);
+        X3 = M(t1 - Z3);
+        Z3 = M(t1 + Z3);
+        Y3 = M(X3 * Z3);
+        t1 = M(t0 + t0); // step 25
+        t1 = M(t1 + t0);
+        t2 = M(a * t2);
+        t4 = M(b3 * t4);
+        t1 = M(t1 + t2);
+        t2 = M(t0 - t2); // step 30
+        t2 = M(a * t2);
+        t4 = M(t4 + t2);
+        t0 = M(t1 * t4);
+        Y3 = M(Y3 + t0);
+        t0 = M(t5 * t4); // step 35
+        X3 = M(t3 * X3);
+        X3 = M(X3 - t0);
+        t0 = M(t3 * t1);
+        Z3 = M(t5 * Z3);
+        Z3 = M(Z3 + t0); // step 40
+        return new Point(X3, Y3, Z3);
+    }
+    /**
+     * Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
+     * Uses {@link wNAF} for base point.
+     * Uses fake point to mitigate side-channel leakage.
+     * @param n scalar by which point is multiplied
+     * @param safe safe mode guards against timing attacks; unsafe mode is faster
+     */
+    multiply(n, safe = true) {
+        if (!safe && n === 0n)
+            return I;
+        agroup(n);
+        if (n === 1n)
+            return this;
+        if (this.equals(G))
+            return wNAF(n).p;
+        // init result point & fake point
+        let p = I;
+        let f = G;
+        for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
+            // if bit is present, add to point
+            // if not present, add to fake, for timing safety
+            if (n & 1n)
+                p = p.add(d);
+            else if (safe)
+                f = f.add(d);
+        }
+        return p;
+    }
+    /** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
+    toAffine() {
+        const { px: x, py: y, pz: z } = this;
+        // fast-paths for ZERO point OR Z=1
+        if (this.equals(I))
+            return { x: 0n, y: 0n };
+        if (z === 1n)
+            return { x, y };
+        const iz = invert(z, P);
+        // (Z * Z^-1) must be 1, otherwise bad math
+        if (M(z * iz) !== 1n)
+            err('inverse invalid');
+        // x = X*Z^-1; y = Y*Z^-1
+        return { x: M(x * iz), y: M(y * iz) };
+    }
+    /** Checks if the point is valid and on-curve. */
+    assertValidity() {
+        const { x, y } = this.toAffine(); // convert to 2d xy affine point.
+        afield(x); // must be in range 1 <= x,y < P
+        afield(y);
+        // y² == x³ + ax + b, equation sides must be equal
+        return M(y * y) === koblitz(x) ? this : err('bad point: not on curve');
+    }
+    /** Converts point to 33/65-byte Uint8Array. */
+    toBytes(isCompressed = true) {
+        const { x, y } = this.assertValidity().toAffine();
+        const x32b = numTo32b(x);
+        if (isCompressed)
+            return concatBytes(getPrefix(y), x32b);
+        return concatBytes(u8of(0x04), x32b, numTo32b(y));
+    }
+    /** Create 3d xyz point from 2d xy. (0, 0) => (0, 1, 0), not (0, 0, 1) */
+    static fromAffine(ap) {
+        const { x, y } = ap;
+        return x === 0n && y === 0n ? I : new Point(x, y, 1n);
+    }
+    toHex(isCompressed) {
+        return bytesToHex(this.toBytes(isCompressed));
+    }
+    static fromPrivateKey(k) {
+        return G.multiply(toPrivScalar(k));
+    }
+    static fromHex(hex) {
+        return Point.fromBytes(toU8(hex));
+    }
+    get x() {
+        return this.toAffine().x;
+    }
+    get y() {
+        return this.toAffine().y;
+    }
+    toRawBytes(isCompressed) {
+        return this.toBytes(isCompressed);
+    }
+}
+/** Generator / base point */
+const G = new Point(Gx, Gy, 1n);
+/** Identity / zero point */
+const I = new Point(0n, 1n, 0n);
+// Static aliases
+Point.BASE = G;
+Point.ZERO = I;
+/** `Q = u1⋅G + u2⋅R`. Verifies Q is not ZERO. Unsafe: non-CT. */
+const doubleScalarMulUns = (R, u1, u2) => {
+    return G.multiply(u1, false).add(R.multiply(u2, false)).assertValidity();
+};
+const bytesToNumBE = (b) => big('0x' + (bytesToHex(b) || '0'));
+const sliceBytesNumBE = (b, from, to) => bytesToNumBE(b.subarray(from, to));
+const B256 = 2n ** 256n; // secp256k1 is weierstrass curve. Equation is x³ + ax + b.
+/** Number to 32b. Must be 0 <= num < B256. validate, pad, to bytes. */
+const numTo32b = (num) => hexToBytes(padh(arange(num, 0n, B256), L2));
+/** Normalize private key to scalar (bigint). Verifies scalar is in range 1 {
+    const num = isBig(pr) ? pr : bytesToNumBE(toU8(pr, L));
+    return arange(num, 1n, N, 'private key invalid 3');
+};
+/** For Signature malleability, validates sig.s is bigger than N/2. */
+const highS = (n) => n > N >> 1n;
+/** Creates 33/65-byte public key from 32-byte private key. */
+const getPublicKey = (privKey, isCompressed = true) => {
+    return G.multiply(toPrivScalar(privKey)).toBytes(isCompressed);
+};
+/** ECDSA Signature class. Supports only compact 64-byte representation, not DER. */
+class Signature {
+    r;
+    s;
+    recovery;
+    constructor(r, s, recovery) {
+        this.r = agroup(r); // 1 <= r < N
+        this.s = agroup(s); // 1 <= s < N
+        if (recovery != null)
+            this.recovery = recovery;
+        Object.freeze(this);
+    }
+    /** Create signature from 64b compact (r || s) representation. */
+    static fromBytes(b) {
+        abytes(b, L2);
+        const r = sliceBytesNumBE(b, 0, L);
+        const s = sliceBytesNumBE(b, L, L2);
+        return new Signature(r, s);
+    }
+    toBytes() {
+        const { r, s } = this;
+        return concatBytes(numTo32b(r), numTo32b(s));
+    }
+    /** Copy signature, with newly added recovery bit. */
+    addRecoveryBit(bit) {
+        return new Signature(this.r, this.s, bit);
+    }
+    hasHighS() {
+        return highS(this.s);
+    }
+    toCompactRawBytes() {
+        return this.toBytes();
+    }
+    toCompactHex() {
+        return bytesToHex(this.toBytes());
+    }
+    recoverPublicKey(msg) {
+        return recoverPublicKey(this, msg);
+    }
+    static fromCompact(hex) {
+        return Signature.fromBytes(toU8(hex, L2));
+    }
+    assertValidity() {
+        return this;
+    }
+    normalizeS() {
+        const { r, s, recovery } = this;
+        return highS(s) ? new Signature(r, modN(-s), recovery) : this;
+    }
+}
+/**
+ * RFC6979: ensure ECDSA msg is X bytes, convert to BigInt.
+ * RFC suggests optional truncating via bits2octets.
+ * FIPS 186-4 4.6 suggests the leftmost min(nBitLen, outLen) bits,
+ * which matches bits2int. bits2int can produce res>N.
+ */
+const bits2int = (bytes) => {
+    const delta = bytes.length * 8 - 256;
+    if (delta > 1024)
+        err('msg invalid'); // our CUSTOM check, "just-in-case": prohibit long inputs
+    const num = bytesToNumBE(bytes);
+    return delta > 0 ? num >> big(delta) : num;
+};
+/** int2octets can't be used; pads small msgs with 0: BAD for truncation as per RFC vectors */
+const bits2int_modN = (bytes) => modN(bits2int(abytes(bytes)));
+const signOpts = { lowS: true };
+const veriOpts = { lowS: true };
+// RFC6979 signature generation, preparation step.
+const prepSig = (msgh, priv, opts = signOpts) => {
+    if (['der', 'recovered', 'canonical'].some((k) => k in opts))
+        // legacy opts
+        err('option not supported');
+    let { lowS, extraEntropy } = opts; // generates low-s sigs by default
+    if (lowS == null)
+        lowS = true; // RFC6979 3.2: we skip step A
+    const i2o = numTo32b; // int to octets
+    const h1i = bits2int_modN(toU8(msgh)); // msg bigint
+    const h1o = i2o(h1i); // msg octets
+    const d = toPrivScalar(priv); // validate private key, convert to bigint
+    const seed = [i2o(d), h1o]; // Step D of RFC6979 3.2
+    /** RFC6979 3.6: additional k' (optional). See {@link ExtraEntropy}. */
+    // K = HMAC_K(V || 0x00 || int2octets(x) || bits2octets(h1) || k')
+    if (extraEntropy)
+        seed.push(extraEntropy === true ? randomBytes(L) : toU8(extraEntropy));
+    const m = h1i; // convert msg to bigint
+    // Converts signature params into point w r/s, checks result for validity.
+    // To transform k => Signature:
+    // q = k⋅G
+    // r = q.x mod n
+    // s = k^-1(m + rd) mod n
+    const k2sig = (kBytes) => {
+        // RFC 6979 Section 3.2, step 3: k = bits2int(T)
+        // Important: all mod() calls here must be done over N
+        const k = bits2int(kBytes);
+        if (!(1n <= k && k < N))
+            return; // Check 0 < k < CURVE.n
+        const q = G.multiply(k).toAffine(); // q = k⋅G
+        const r = modN(q.x); // r = q.x mod n
+        if (r === 0n)
+            return;
+        const ik = invert(k, N); // k^-1 mod n, NOT mod P
+        const s = modN(ik * modN(m + modN(d * r))); // s = k^-1(m + rd) mod n
+        if (s === 0n)
+            return;
+        let normS = s; // normalized S
+        let recovery = (q.x === r ? 0 : 2) | Number(q.y & 1n); // recovery bit (2 or 3, when q.x > n)
+        if (lowS && highS(s)) {
+            // if lowS was passed, ensure s is always
+            normS = modN(-s); // in the bottom half of CURVE.n
+            recovery ^= 1;
+        }
+        return new Signature(r, normS, recovery); // use normS, not s
+    };
+    return { seed: concatBytes(...seed), k2sig };
+};
+// HMAC-DRBG from NIST 800-90. Minimal, non-full-spec - used for RFC6979 signatures.
+const hmacDrbg = (asynchronous) => {
+    let v = u8n(L); // Steps B, C of RFC6979 3.2: set hashLen
+    let k = u8n(L); // In our case, it's always equal to L
+    let i = 0; // Iterations counter, will throw when over max
+    const NULL = u8n(0);
+    const reset = () => {
+        v.fill(1);
+        k.fill(0);
+        i = 0;
+    };
+    const max = 1000;
+    const _e = 'drbg: tried 1000 values';
+    if (asynchronous) {
+        // asynchronous=true
+        // h = hmac(K || V || ...)
+        const h = (...b) => etc.hmacSha256Async(k, v, ...b);
+        const reseed = async (seed = NULL) => {
+            // HMAC-DRBG reseed() function. Steps D-G
+            k = await h(u8of(0x00), seed); // k = hmac(K || V || 0x00 || seed)
+            v = await h(); // v = hmac(K || V)
+            if (seed.length === 0)
+                return;
+            k = await h(u8of(0x01), seed); // k = hmac(K || V || 0x01 || seed)
+            v = await h(); // v = hmac(K || V)
+        };
+        // HMAC-DRBG generate() function
+        const gen = async () => {
+            if (i++ >= max)
+                err(_e);
+            v = await h(); // v = hmac(K || V)
+            return v; // this diverges from noble-curves: we don't allow arbitrary output len!
+        };
+        // Do not reuse returned fn for more than 1 sig:
+        // 1) it's slower (JIT screws up). 2. unsafe (async race conditions)
+        return async (seed, pred) => {
+            reset();
+            await reseed(seed); // Steps D-G
+            let res = undefined; // Step H: grind until k is in [1..n-1]
+            while (!(res = pred(await gen())))
+                await reseed(); // test predicate until it returns ok
+            reset();
+            return res;
+        };
+    }
+    else {
+        // asynchronous=false; same as above, but synchronous
+        // h = hmac(K || V || ...)
+        const h = (...b) => callHash('hmacSha256Sync')(k, v, ...b);
+        const reseed = (seed = NULL) => {
+            // HMAC-DRBG reseed() function. Steps D-G
+            k = h(u8of(0x00), seed); // k = hmac(k || v || 0x00 || seed)
+            v = h(); // v = hmac(k || v)
+            if (seed.length === 0)
+                return;
+            k = h(u8of(0x01), seed); // k = hmac(k || v || 0x01 || seed)
+            v = h(); // v = hmac(k || v)
+        };
+        // HMAC-DRBG generate() function
+        const gen = () => {
+            if (i++ >= max)
+                err(_e);
+            v = h(); // v = hmac(k || v)
+            return v; // this diverges from noble-curves: we don't allow arbitrary output len!
+        };
+        // Do not reuse returned fn for more than 1 sig:
+        // 1) it's slower (JIT screws up). 2. unsafe (async race conditions)
+        return (seed, pred) => {
+            reset();
+            reseed(seed); // Steps D-G
+            let res = undefined; // Step H: grind until k is in [1..n-1]
+            while (!(res = pred(gen())))
+                reseed(); // test predicate until it returns ok
+            reset();
+            return res;
+        };
+    }
+};
+/**
+ * Sign a msg hash using secp256k1. Async.
+ * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.2 & RFC6979.
+ * It's suggested to enable hedging ({@link ExtraEntropy}) to prevent fault attacks.
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
+ * @param priv - private key
+ * @param opts - `lowS: true` prevents malleability, `extraEntropy: true` enables hedging
+ */
+const signAsync = async (msgh, priv, opts = signOpts) => {
+    // Re-run drbg until k2sig returns ok
+    const { seed, k2sig } = prepSig(msgh, priv, opts);
+    const sig = await hmacDrbg(true)(seed, k2sig);
+    return sig;
+};
+/**
+ * Sign a msg hash using secp256k1.
+ * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.2 & RFC6979.
+ * It's suggested to enable hedging ({@link ExtraEntropy}) to prevent fault attacks.
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
+ * @param priv - private key
+ * @param opts - `lowS: true` prevents malleability, `extraEntropy: true` enables hedging
+ * @example
+ * const sig = sign(sha256('hello'), privKey, { extraEntropy: true }).toBytes();
+ */
+const sign = (msgh, priv, opts = signOpts) => {
+    // Re-run drbg until k2sig returns ok
+    const { seed, k2sig } = prepSig(msgh, priv, opts);
+    const sig = hmacDrbg(false)(seed, k2sig);
+    return sig;
+};
+/**
+ * Verify a signature using secp256k1.
+ * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.4.
+ * Default lowS=true, prevents malleability.
+ * @param sig - signature, 64-byte or Signature instance
+ * @param msgh - message HASH, not message itself e.g. sha256(message)
+ * @param pub - public key
+ * @param opts - { lowS: true } is default, prohibits s >= CURVE.n/2 to prevent malleability
+ */
+const verify = (sig, msgh, pub, opts = veriOpts) => {
+    let { lowS } = opts;
+    if (lowS == null)
+        lowS = true;
+    if ('strict' in opts)
+        err('option not supported');
+    let sigg;
+    // Previous ver supported DER sigs.
+    // We throw error when DER is suspected now.
+    const rs = sig && typeof sig === 'object' && 'r' in sig;
+    if (!rs && toU8(sig).length !== L2)
+        err('signature must be 64 bytes');
+    try {
+        sigg = rs ? new Signature(sig.r, sig.s) : Signature.fromCompact(sig);
+        const h = bits2int_modN(toU8(msgh)); // Truncate hash
+        const P = Point.fromBytes(toU8(pub)); // Validate public key
+        const { r, s } = sigg;
+        if (lowS && highS(s))
+            return false; // lowS bans sig.s >= CURVE.n/2
+        const is = invert(s, N); // s^-1
+        const u1 = modN(h * is); // u1 = hs^-1 mod n
+        const u2 = modN(r * is); // u2 = rs^-1 mod n
+        const R = doubleScalarMulUns(P, u1, u2).toAffine(); // R = u1⋅G + u2⋅P
+        // Stop if R is identity / zero point. Check is done inside `doubleScalarMulUns`
+        const v = modN(R.x); // R.x must be in N's field, not P's
+        return v === r; // mod(R.x, n) == r
+    }
+    catch (error) {
+        return false;
+    }
+};
+/**
+ * ECDSA public key recovery. Requires msg hash and recovery id.
+ * Follows [SEC1](https://secg.org/sec1-v2.pdf) 4.1.6.
+ */
+const recoverPublicKey = (sig, msgh) => {
+    const { r, s, recovery } = sig;
+    // 0 or 1 recovery id determines sign of "y" coordinate.
+    // 2 or 3 means q.x was >N.
+    if (![0, 1, 2, 3].includes(recovery))
+        err('recovery id invalid');
+    const h = bits2int_modN(toU8(msgh, L)); // Truncate hash
+    const radj = recovery === 2 || recovery === 3 ? r + N : r;
+    afield(radj); // ensure q.x is still a field element
+    const head = getPrefix(big(recovery)); // head is 0x02 or 0x03
+    const Rb = concatBytes(head, numTo32b(radj)); // concat head + r
+    const R = Point.fromBytes(Rb);
+    const ir = invert(radj, N); // r^-1
+    const u1 = modN(-h * ir); // -hr^-1
+    const u2 = modN(s * ir); // sr^-1
+    return doubleScalarMulUns(R, u1, u2); // (sr^-1)R-(hr^-1)G = -(hr^-1)G + (sr^-1)
+};
+/**
+ * Elliptic Curve Diffie-Hellman (ECDH) on secp256k1.
+ * Result is **NOT hashed**. Use hash or KDF on it if you need.
+ * @param privA private key A
+ * @param pubB public key B
+ * @param isCompressed 33-byte (true) or 65-byte (false) output
+ * @returns public key C
+ */
+const getSharedSecret = (privA, pubB, isCompressed = true) => {
+    return Point.fromBytes(toU8(pubB)).multiply(toPrivScalar(privA)).toBytes(isCompressed);
+};
+// FIPS 186 B.4.1 compliant key generation produces private keys with modulo bias being neglible.
+// takes >N+8 bytes, returns (hash mod n-1)+1
+const hashToPrivateKey = (hash) => {
+    hash = toU8(hash);
+    if (hash.length < L + 8 || hash.length > 1024)
+        err('expected 40-1024b');
+    const num = M(bytesToNumBE(hash), N - 1n);
+    return numTo32b(num + 1n);
+};
+const randomPrivateKey = () => hashToPrivateKey(randomBytes(L + 16)); // FIPS 186 B.4.1.
+const _sha = 'SHA-256';
+/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
+const etc = {
+    hexToBytes: hexToBytes,
+    bytesToHex: bytesToHex,
+    concatBytes: concatBytes,
+    bytesToNumberBE: bytesToNumBE,
+    numberToBytesBE: numTo32b,
+    mod: M,
+    invert: invert, // math utilities
+    hmacSha256Async: async (key, ...msgs) => {
+        const s = subtle();
+        const name = 'HMAC';
+        const k = await s.importKey('raw', key, { name, hash: { name: _sha } }, false, ['sign']);
+        return u8n(await s.sign(name, k, concatBytes(...msgs)));
+    },
+    hmacSha256Sync: undefined, // For TypeScript. Actual logic is below
+    hashToPrivateKey: hashToPrivateKey,
+    randomBytes: randomBytes,
+};
+/** Curve-specific utilities for private keys. */
+const utils = {
+    normPrivateKeyToScalar: toPrivScalar,
+    isValidPrivateKey: (key) => {
+        try {
+            return !!toPrivScalar(key);
+        }
+        catch (e) {
+            return false;
+        }
+    },
+    randomPrivateKey: randomPrivateKey,
+    precompute: (w = 8, p = G) => {
+        p.multiply(3n);
+        w;
+        return p;
+    },
+};
+// ## Precomputes
+// --------------
+const W = 8; // W is window size
+const scalarBits = 256;
+const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8
+const pwindowSize = 2 ** (W - 1); // 128 for W=8
+const precompute = () => {
+    const points = [];
+    let p = G;
+    let b = p;
+    for (let w = 0; w < pwindows; w++) {
+        b = p;
+        points.push(b);
+        for (let i = 1; i < pwindowSize; i++) {
+            b = b.add(p);
+            points.push(b);
+        } // i=1, bc we skip 0
+        p = b.double();
+    }
+    return points;
+};
+let Gpows = undefined; // precomputes for base point G
+// const-time negate
+const ctneg = (cnd, p) => {
+    const n = p.negate();
+    return cnd ? n : p;
+};
+/**
+ * Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by
+ * caching multiples of G (base point). Cache is stored in 32MB of RAM.
+ * Any time `G.multiply` is done, precomputes are used.
+ * Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.
+ *
+ * w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,
+ * but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.
+ *
+ * !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
+ */
+const wNAF = (n) => {
+    const comp = Gpows || (Gpows = precompute());
+    let p = I;
+    let f = G; // f must be G, or could become I in the end
+    const pow_2_w = 2 ** W; // 256 for W=8
+    const maxNum = pow_2_w; // 256 for W=8
+    const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111
+    const shiftBy = big(W); // 8 for W=8
+    for (let w = 0; w < pwindows; w++) {
+        let wbits = Number(n & mask); // extract W bits.
+        n >>= shiftBy; // shift number by W bits.
+        if (wbits > pwindowSize) {
+            wbits -= maxNum;
+            n += 1n;
+        } // split if bits > max: +224 => 256-32
+        const off = w * pwindowSize;
+        const offF = off; // offsets, evaluate both
+        const offP = off + Math.abs(wbits) - 1;
+        const isEven = w % 2 !== 0; // conditions, evaluate both
+        const isNeg = wbits < 0;
+        if (wbits === 0) {
+            // off == I: can't add it. Adding random offF instead.
+            f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point
+        }
+        else {
+            p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
+        }
+    }
+    return { p, f }; // return both real and fake points for JIT
+};
+// !! Remove the export below to easily use in REPL / browser console
+export { secp256k1_CURVE as CURVE, etc, getPublicKey, getSharedSecret, Point, Point as ProjectivePoint, sign, signAsync, Signature, utils, verify, };