From 57d3bbe081757ce3c473302e94e9f529ca41b5e4 Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 14 Apr 2024 12:54:33 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20=F0=9F=A4=96=20remove=20web3=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/web3/adl/feed-crdt/Feed.ts | 321 -------------- src/web3/adl/feed-crdt/FeedFactory.ts | 19 - src/web3/adl/feed-crdt/FeedFrame.ts | 36 -- src/web3/adl/feed-crdt/README.md | 10 - .../feed-crdt/__tests__/Feed-merge.spec.ts | 391 ------------------ src/web3/adl/feed-crdt/__tests__/Feed.spec.ts | 210 ---------- src/web3/adl/feed-crdt/constants.ts | 13 - src/web3/adl/feed-crdt/types.ts | 37 -- src/web3/adl/hamt-crdt/Hamt.ts | 106 ----- src/web3/adl/hamt-crdt/HamtFactory.ts | 12 - src/web3/adl/hamt-crdt/HamtFrame.ts | 217 ---------- src/web3/adl/hamt-crdt/README.md | 10 - src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts | 265 ------------ src/web3/adl/hamt-crdt/constants.ts | 3 - src/web3/adl/hamt-crdt/types.ts | 64 --- src/web3/codec/Codecs.ts | 20 - src/web3/codec/codecs/__tests__/cbor.spec.ts | 22 - src/web3/codec/codecs/cbor.ts | 26 -- src/web3/codec/codecs/raw.ts | 19 - src/web3/codec/codecs/writer.ts | 3 - src/web3/codec/index.ts | 13 - src/web3/codec/types.ts | 7 - src/web3/constants.ts | 4 - src/web3/crypto/index.ts | 2 - src/web3/crypto/sha256.ts | 6 - src/web3/crypto/webcrypto.ts | 8 - src/web3/hlc/Hlc.ts | 16 - src/web3/hlc/HlcFactory.ts | 37 -- src/web3/hlc/README.md | 7 - src/web3/hlc/__tests__/HlcFactory.spec.ts | 20 - src/web3/hlc/__tests__/util.spec.ts | 151 ------- src/web3/hlc/index.ts | 4 - src/web3/hlc/types.ts | 14 - src/web3/hlc/util.ts | 34 -- src/web3/multiformats/Cid.ts | 121 ------ src/web3/multiformats/Multihash.ts | 68 --- src/web3/multiformats/__tests__/Cid.spec.ts | 73 ---- .../multiformats/__tests__/Multihash.spec.ts | 56 --- src/web3/multiformats/constants.ts | 36 -- src/web3/multiformats/index.ts | 2 - src/web3/multiformats/multibase.ts | 9 - src/web3/store/cas/CidCas.ts | 9 - src/web3/store/cas/CidCasMemory.ts | 39 -- src/web3/store/cas/CidCasStruct.ts | 29 -- src/web3/store/cas/CidCasStructCbor.ts | 14 - src/web3/store/cas/README.md | 4 - .../store/cas/__tests__/CidCasMemory.spec.ts | 58 --- .../cas/__tests__/CidCasStructCbor.spec.ts | 64 --- src/web3/util/__tests__/uvint.spec.ts | 58 --- src/web3/util/uvint.ts | 46 --- 50 files changed, 2813 deletions(-) delete mode 100644 src/web3/adl/feed-crdt/Feed.ts delete mode 100644 src/web3/adl/feed-crdt/FeedFactory.ts delete mode 100644 src/web3/adl/feed-crdt/FeedFrame.ts delete mode 100644 src/web3/adl/feed-crdt/README.md delete mode 100644 src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts delete mode 100644 src/web3/adl/feed-crdt/__tests__/Feed.spec.ts delete mode 100644 src/web3/adl/feed-crdt/constants.ts delete mode 100644 src/web3/adl/feed-crdt/types.ts delete mode 100644 src/web3/adl/hamt-crdt/Hamt.ts delete mode 100644 src/web3/adl/hamt-crdt/HamtFactory.ts delete mode 100644 src/web3/adl/hamt-crdt/HamtFrame.ts delete mode 100644 src/web3/adl/hamt-crdt/README.md delete mode 100644 src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts delete mode 100644 src/web3/adl/hamt-crdt/constants.ts delete mode 100644 src/web3/adl/hamt-crdt/types.ts delete mode 100644 src/web3/codec/Codecs.ts delete mode 100644 src/web3/codec/codecs/__tests__/cbor.spec.ts delete mode 100644 src/web3/codec/codecs/cbor.ts delete mode 100644 src/web3/codec/codecs/raw.ts delete mode 100644 src/web3/codec/codecs/writer.ts delete mode 100644 src/web3/codec/index.ts delete mode 100644 src/web3/codec/types.ts delete mode 100644 src/web3/constants.ts delete mode 100644 src/web3/crypto/index.ts delete mode 100644 src/web3/crypto/sha256.ts delete mode 100644 src/web3/crypto/webcrypto.ts delete mode 100644 src/web3/hlc/Hlc.ts delete mode 100644 src/web3/hlc/HlcFactory.ts delete mode 100644 src/web3/hlc/README.md delete mode 100644 src/web3/hlc/__tests__/HlcFactory.spec.ts delete mode 100644 src/web3/hlc/__tests__/util.spec.ts delete mode 100644 src/web3/hlc/index.ts delete mode 100644 src/web3/hlc/types.ts delete mode 100644 src/web3/hlc/util.ts delete mode 100644 src/web3/multiformats/Cid.ts delete mode 100644 src/web3/multiformats/Multihash.ts delete mode 100644 src/web3/multiformats/__tests__/Cid.spec.ts delete mode 100644 src/web3/multiformats/__tests__/Multihash.spec.ts delete mode 100644 src/web3/multiformats/constants.ts delete mode 100644 src/web3/multiformats/index.ts delete mode 100644 src/web3/multiformats/multibase.ts delete mode 100644 src/web3/store/cas/CidCas.ts delete mode 100644 src/web3/store/cas/CidCasMemory.ts delete mode 100644 src/web3/store/cas/CidCasStruct.ts delete mode 100644 src/web3/store/cas/CidCasStructCbor.ts delete mode 100644 src/web3/store/cas/README.md delete mode 100644 src/web3/store/cas/__tests__/CidCasMemory.spec.ts delete mode 100644 src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts delete mode 100644 src/web3/util/__tests__/uvint.spec.ts delete mode 100644 src/web3/util/uvint.ts diff --git a/src/web3/adl/feed-crdt/Feed.ts b/src/web3/adl/feed-crdt/Feed.ts deleted file mode 100644 index 2eb3a8bbf4..0000000000 --- a/src/web3/adl/feed-crdt/Feed.ts +++ /dev/null @@ -1,321 +0,0 @@ -import {FeedFrame} from './FeedFrame'; -import {AvlSet} from '../../../util/trees/avl/AvlSet'; -import {AvlMap} from '../../../util/trees/avl/AvlMap'; -import {Cid} from '../../multiformats'; -import {mutex} from 'thingies/es2020/mutex'; -import {FanOut} from 'thingies/es2020/fanout'; -import {FeedConstraints, FeedOpType} from './constants'; -import * as hlc from '../../hlc'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type * as types from './types'; -import type {SyncStore} from '../../../util/events/sync-store'; - -export interface FeedDependencies { - cas: CidCasStruct; - hlcs: hlc.HlcFactory; - - /** - * Number of operations after which a new frame is created, otherwise the - * operations are appended to the current frame. Defaults to 25. - */ - opsPerFrame?: number; -} - -export class Feed implements types.FeedApi, SyncStore { - public static async merge( - cas: CidCasStruct, - baseCid: Cid, - forkCid: Cid, - opsPerFrame: number = FeedConstraints.DefaultOpsPerFrameThreshold, - ): Promise { - const [commonParent, baseFrames, forkFrames] = await Feed.findForkTriangle(cas, baseCid, forkCid); - const ops = Feed.zipOps(baseFrames, forkFrames); - let lastFrame: FeedFrame | null = commonParent; - const frames: FeedFrame[] = []; - while (ops.length) { - const frameOps = ops.splice(0, opsPerFrame); - const prev = lastFrame ? lastFrame.cid.toBinaryV1() : null; - const seq = lastFrame ? lastFrame.seq() + 1 : FeedConstraints.FirstFrameSeq; - const dto: types.FeedFrameDto = [prev, seq, frameOps]; - const frame = await FeedFrame.create(dto, cas); - frame.prev = lastFrame; - lastFrame = frame; - frames.push(frame); - } - return frames; - } - - protected static zipOps(baseFrames: FeedFrame[], forkFrames: FeedFrame[]): types.FeedOp[] { - const baseOps: types.FeedOp[] = []; - const forkOps: types.FeedOp[] = []; - for (const frame of baseFrames) baseOps.push(...frame.ops()); - for (const frame of forkFrames) forkOps.push(...frame.ops()); - const ops: types.FeedOp[] = []; - while (baseOps.length || forkOps.length) { - if (!baseOps.length) { - ops.push(...forkOps); - break; - } - if (!forkOps.length) { - ops.push(...baseOps); - break; - } - const baseOp = baseOps[0]; - if (baseOp[0] === FeedOpType.Delete) { - ops.push(baseOp); - baseOps.shift(); - continue; - } - const forkOp = forkOps[0]; - if (forkOp[0] === FeedOpType.Delete) { - ops.push(forkOp); - forkOps.shift(); - continue; - } - const baseId = baseOp[1]; - const forkId = forkOp[1]; - const cmp = hlc.cmpDto(baseId, forkId); - if (cmp === 0) { - ops.push(baseOp); - baseOps.shift(); - forkOps.shift(); - continue; - } else if (cmp < 0) { - ops.push(baseOp); - baseOps.shift(); - continue; - } else { - ops.push(forkOp); - forkOps.shift(); - continue; - } - } - return ops; - } - - protected static async findForkTriangle( - cas: CidCasStruct, - leftCid: Cid, - rightCid: Cid, - ): Promise<[commonParent: FeedFrame | null, leftFrames: FeedFrame[], rightFrames: FeedFrame[]]> { - const leftHeadFrame = await FeedFrame.read(leftCid, cas); - const rightHeadFrame = await FeedFrame.read(rightCid, cas); - const leftFrames: FeedFrame[] = [leftHeadFrame]; - const rightFrames: FeedFrame[] = [rightHeadFrame]; - if (leftHeadFrame.seq() > rightHeadFrame.seq()) { - while (true) { - const prevCid = leftFrames[leftFrames.length - 1].prevCid(); - if (!prevCid) throw new Error('INVALID_STATE'); - const cid = Cid.fromBinaryV1(prevCid); - const frame = await FeedFrame.read(cid, cas); - leftFrames.push(frame); - if (frame.seq() <= rightHeadFrame.seq()) break; - } - } - if (leftHeadFrame.seq() < rightHeadFrame.seq()) { - while (true) { - const prevCid = rightFrames[rightFrames.length - 1].prevCid(); - if (!prevCid) throw new Error('INVALID_STATE'); - const cid = Cid.fromBinaryV1(prevCid); - const frame = await FeedFrame.read(cid, cas); - rightFrames.push(frame); - if (frame.seq() <= leftHeadFrame.seq()) break; - } - } - while (true) { - const leftFrame = leftFrames[leftFrames.length - 1]; - const rightFrame = rightFrames[rightFrames.length - 1]; - if (leftFrame.seq() !== rightFrame.seq()) throw new Error('INVALID_STATE'); - if (leftFrame.seq() === 0) return [null, leftFrames, rightFrames]; - if (leftFrame.cid.is(rightFrame.cid)) { - leftFrames.pop(); - rightFrames.pop(); - return [leftFrame, leftFrames, rightFrames]; - } - const prevLeft = leftFrame.prevCid(); - const prevRight = rightFrame.prevCid(); - if (!prevLeft || !prevRight) throw new Error('INVALID_STATE'); - leftFrames.push(await FeedFrame.read(Cid.fromBinaryV1(prevLeft), cas)); - rightFrames.push(await FeedFrame.read(Cid.fromBinaryV1(prevRight), cas)); - } - } - - /** - * Number of operations after which a new frame is created, otherwise the - * operations are appended to the current frame. - */ - public opsPerFrame: number; - - /** - * Emitted when the feed view changes (new entries are added or deleted). - */ - public onChange: FanOut = new FanOut(); - - protected head: FeedFrame | null = null; - protected tail: FeedFrame | null = null; - protected unsaved: types.FeedOp[] = []; - protected readonly deletes = new AvlSet(hlc.cmpDto); - protected readonly inserts = new AvlMap(hlc.cmpDto); - - constructor(protected readonly deps: FeedDependencies) { - this.opsPerFrame = deps.opsPerFrame ?? FeedConstraints.DefaultOpsPerFrameThreshold; - } - - public cid(): Cid | undefined { - return this.head?.cid; - } - - public async loadAll(): Promise { - while (this.hasMore()) await this.loadMore(); - } - - public clear(): void { - this.head = null; - this.tail = null; - this.unsaved = []; - this.deletes.clear(); - if (!this.inserts.isEmpty()) { - this.inserts.clear(); - this.onChange.emit(); - } - } - - public async merge(forkCid: Cid): Promise { - if (this.unsaved.length) await this.save(); - if (!this.head) throw new Error('INVALID_STATE'); - const frames = await Feed.merge(this.deps.cas, this.head.cid, forkCid, this.opsPerFrame); - for (const frame of frames) this.ingestFrameData(frame, true); - const head = frames[frames.length - 1]; - let curr = head; - for (let i = frames.length - 2; i >= 0; i--) { - curr.prev = frames[i]; - curr = frames[i]; - } - let existingCurr: FeedFrame | null = this.head; - while (existingCurr && existingCurr.seq() > curr.seq()) existingCurr = existingCurr.prev; - if (existingCurr) curr.prev = existingCurr.prev; - else this.tail = curr; - this.head = head; - this.onChange.emit(); - } - - // ------------------------------------------------------------------ FeedApi - - public add(data: unknown): hlc.HlcDto { - const id = this.deps.hlcs.inc(); - const idDto = hlc.toDto(id); - const op: types.FeedOpInsert = [FeedOpType.Insert, idDto, data]; - this.unsaved.push(op); - this.inserts.set(op[1], op); - this.onChange.emit(); - return idDto; - } - - public del(opId: hlc.HlcDto): void { - const op: types.FeedOpDelete = [FeedOpType.Delete, opId]; - this.unsaved.push(op); - this.deletes.add(opId); - const unsavedOpIndex = this.unsaved.findIndex( - ([type, id]) => type === FeedOpType.Insert && hlc.cmpDto(opId, id) === 0, - ); - if (unsavedOpIndex !== -1) this.unsaved.splice(unsavedOpIndex, 1); - const deleted = this.inserts.del(opId); - if (deleted) this.onChange.emit(); - } - - @mutex - public async loadHead(cid: Cid): Promise { - this.clear(); - const frame = await FeedFrame.read(cid, this.deps.cas); - this.head = this.tail = frame; - this.ingestFrameData(frame); - } - - @mutex - public async loadMore(): Promise { - const tail = this.tail; - if (!tail) return; - const prevCidDto = tail.data[0]; - if (!prevCidDto) return; - const cid = Cid.fromBinaryV1(prevCidDto); - const frame = this.tail?.prev ?? (await FeedFrame.read(cid, this.deps.cas)); - tail.prev = frame; - this.tail = frame; - this.ingestFrameData(frame); - } - - public hasMore(): boolean { - return !!this.tail?.data[0]; - } - - protected ingestFrameData(frame: FeedFrame, silent?: boolean): void { - const [, , ops] = frame.data; - for (const op of ops) { - switch (op[0]) { - case FeedOpType.Insert: { - const id = op[1]; - if (this.deletes.has(id)) continue; - this.inserts.set(id, op); - break; - } - case FeedOpType.Delete: { - const id = op[1]; - this.deletes.add(id); - this.inserts.del(id); - break; - } - } - } - if (!silent) this.onChange.emit(); - } - - @mutex - public async save(): Promise { - const hasUnsavedChanges = !!this.unsaved.length; - const head = this.head; - if (!hasUnsavedChanges) { - if (head) return head.cid; - const dto: types.FeedFrameDto = [null, 0, []]; - const frame = await FeedFrame.create(dto, this.deps.cas); - this.head = this.tail = frame; - this.unsaved = []; - return frame.cid; - } - if (!head) { - const dto: types.FeedFrameDto = [null, 0, this.unsaved]; - const frame = await FeedFrame.create(dto, this.deps.cas); - this.head = this.tail = frame; - this.unsaved = []; - return frame.cid; - } - const headOps = head.ops(); - const addToHead = headOps.length < this.opsPerFrame; - if (addToHead) { - const dto: types.FeedFrameDto = [head.prevCid(), head.seq(), [...headOps, ...this.unsaved]]; - const frame = await FeedFrame.create(dto, this.deps.cas); - frame.prev = head.prev; - this.head = frame; - this.unsaved = []; - return frame.cid; - } - const dto: types.FeedFrameDto = [head.cid.toBinaryV1(), head.seq() + 1, this.unsaved]; - const frame = await FeedFrame.create(dto, this.deps.cas); - frame.prev = head; - this.head = frame; - this.unsaved = []; - return frame.cid; - } - - // ---------------------------------------------------------------- SyncStore - - public readonly subscribe = (callback: () => void) => { - const unsubscribe = this.onChange.listen(() => callback()); - return () => unsubscribe(); - }; - - public readonly getSnapshot = (): types.FeedOpInsert[] => { - const ops: types.FeedOpInsert[] = []; - this.inserts.forEach((node) => ops.push(node.v)); - return ops; - }; -} diff --git a/src/web3/adl/feed-crdt/FeedFactory.ts b/src/web3/adl/feed-crdt/FeedFactory.ts deleted file mode 100644 index 5e077d55b6..0000000000 --- a/src/web3/adl/feed-crdt/FeedFactory.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {Feed, type FeedDependencies} from './Feed'; -import type {Cid} from '../../multiformats'; - -export interface FeedFactoryDependencies extends FeedDependencies {} - -export class FeedFactory { - constructor(protected readonly deps: FeedFactoryDependencies) {} - - public make(): Feed { - const feed = new Feed(this.deps); - return feed; - } - - public async load(cid: Cid): Promise { - const feed = new Feed(this.deps); - await feed.loadHead(cid); - return feed; - } -} diff --git a/src/web3/adl/feed-crdt/FeedFrame.ts b/src/web3/adl/feed-crdt/FeedFrame.ts deleted file mode 100644 index 4c6841a07f..0000000000 --- a/src/web3/adl/feed-crdt/FeedFrame.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {Cid} from '../../multiformats'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type {FeedFrameDto, FeedOp} from './types'; - -export class FeedFrame { - public static async read(cid: Cid, cas: CidCasStruct): Promise { - const dto = (await cas.get(cid)) as FeedFrameDto; - const frame = new FeedFrame(cid, dto); - return frame; - } - - public static async create(data: FeedFrameDto, cas: CidCasStruct): Promise { - const cid = await cas.put(data); - const frame = new FeedFrame(cid, data); - return frame; - } - - public prev: FeedFrame | null = null; - - constructor( - public cid: Cid, - public data: FeedFrameDto, - ) {} - - public prevCid(): Uint8Array | null { - return this.data[0]; - } - - public seq(): number { - return this.data[1]; - } - - public ops(): FeedOp[] { - return this.data[2]; - } -} diff --git a/src/web3/adl/feed-crdt/README.md b/src/web3/adl/feed-crdt/README.md deleted file mode 100644 index 0451cbafcc..0000000000 --- a/src/web3/adl/feed-crdt/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Feed CRDT - -Feed CRDT is an infinite length sorted list of entries. It supports only -*insert* and *delete* operations. A user can append a new entry to the end of -the list (insert), or delete an existing entry from the list. - -Entries are stored in *frames*. The feed is represented as a linked list of -frames. Each frame stores one or more operations. There are insert and delete -operations. Each operation is identified by a hybrid logical clock (HLC) -timestamp. diff --git a/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts b/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts deleted file mode 100644 index dab1eae8de..0000000000 --- a/src/web3/adl/feed-crdt/__tests__/Feed-merge.spec.ts +++ /dev/null @@ -1,391 +0,0 @@ -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {Feed} from '../Feed'; -import {FeedFactory, FeedFactoryDependencies} from '../FeedFactory'; -import {FeedFrame} from '../FeedFrame'; - -interface SetupOpts { - factory?: Pick; -} - -const setup = ({factory}: SetupOpts = {}) => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const feeds = new FeedFactory({...factory, hlcs, cas}); - return { - hlcs, - cas0, - cas, - feeds, - }; -}; - -describe('can merge', () => { - test('when common parent is null', async () => { - const {feeds, cas} = setup(); - const feed = feeds.make(); - feed.add({foo: 'bar'}); - const cid = await feed.save(); - const left = await feeds.load(cid); - const right = await feeds.load(cid); - expect(left.getSnapshot().length).toBe(1); - expect(left.getSnapshot()).toStrictEqual(right.getSnapshot()); - left.add('left1'); - left.add('left2'); - right.add('right1'); - const leftCid = await left.save(); - const rightCid = await right.save(); - const res = await Feed.merge(cas, leftCid, rightCid); - const mergedFeed = await feeds.load(res[0].cid); - expect(mergedFeed.getSnapshot().length).toBe(4); - expect(mergedFeed.getSnapshot()[0][2]).toStrictEqual({foo: 'bar'}); - expect(mergedFeed.getSnapshot()[1][2]).toStrictEqual('left1'); - expect(mergedFeed.getSnapshot()[2][2]).toStrictEqual('left2'); - expect(mergedFeed.getSnapshot()[3][2]).toStrictEqual('right1'); - }); - - const generateCommonParent = async () => { - const deps = setup({factory: {opsPerFrame: 3}}); - const common = deps.feeds.make(); - common.add('c1'); - common.add('c2'); - common.add('c3'); - await common.save(); - common.add('c4'); - common.add('c5'); - common.add('c6'); - await common.save(); - common.add('c7'); - common.add('c8'); - common.add('c9'); - await common.save(); - return {...deps, common}; - }; - - test('when both sides one step from common parent', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'r1', - ]); - }); - - test('when left side is further ahead', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - left.add('l4'); - left.add('l5'); - left.add('l6'); - await left.save(); - left.add('l7'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'l4', - 'l5', - 'l6', - 'l7', - 'r1', - 'r2', - ]); - }); - - test('when right side has not advanced', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - left.add('l4'); - left.add('l5'); - left.add('l6'); - await left.save(); - left.add('l7'); - await left.save(); - const frames = await Feed.merge(cas, left.cid()!, common.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'l4', - 'l5', - 'l6', - 'l7', - ]); - }); - - test('when right side is further ahead', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - await right.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'l1', - 'l2', - 'l3', - 'r1', - 'r2', - 'r3', - 'r4', - ]); - }); - - test('when right side is further ahead and applied first', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - await right.save(); - const left = await feeds.load(common.cid()!); - left.add('l1'); - left.add('l2'); - left.add('l3'); - await left.save(); - const frames = await Feed.merge(cas, left.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'r1', - 'r2', - 'r3', - 'r4', - 'l1', - 'l2', - 'l3', - ]); - }); - - test('when left side has not advanced', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const right = await feeds.load(common.cid()!); - right.add('r1'); - right.add('r2'); - right.add('r3'); - await right.save(); - right.add('r4'); - right.add('r5'); - right.add('r6'); - await right.save(); - right.add('r7'); - await right.save(); - const frames = await Feed.merge(cas, common.cid()!, right.cid()!, 3); - expect(frames.length).toBe(3); - const merged = await feeds.load(frames[frames.length - 1].cid); - await merged.loadAll(); - expect(merged.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'r1', - 'r2', - 'r3', - 'r4', - 'r5', - 'r6', - 'r7', - ]); - }); -}); - -describe('can merge inline', () => { - const generateCommonParent = async () => { - const deps = setup({factory: {opsPerFrame: 3}}); - const common = deps.feeds.make(); - common.add('c1'); - common.add('c2'); - common.add('c3'); - await common.save(); - common.add('c4'); - common.add('c5'); - common.add('c6'); - await common.save(); - common.add('c7'); - common.add('c8'); - common.add('c9'); - await common.save(); - return {...deps, common}; - }; - - test('when both sides one step from common parent', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const fork = await feeds.load(common.cid()!); - fork.add('f1'); - await fork.save(); - common.add('c10'); - await common.save(); - await common.merge(fork.cid()!); - expect(common.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - ]); - let frame: null | FeedFrame = (common as any).head; - let seq = frame?.seq() ?? 0; - while (frame) { - expect(frame.seq()).toBe(seq); - seq--; - frame = frame.prev; - } - }); - - test('can merge forks twice', async () => { - const {feeds, cas, common} = await generateCommonParent(); - const fork = await feeds.load(common.cid()!); - fork.add('f1'); - await fork.save(); - common.add('c10'); - await common.save(); - await common.merge(fork.cid()!); - const fork2 = await feeds.load(common.cid()!); - common.add('c11'); - common.add('c12'); - fork2.add('f2'); - fork2.add('f3'); - fork2.add('f4'); - await fork2.save(); - await common.merge(fork2.cid()!); - expect(common.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - 'c11', - 'c12', - 'f2', - 'f3', - 'f4', - ]); - const common2 = await feeds.load(common.cid()!); - await common2.loadAll(); - expect(common2.getSnapshot().map(([, , data]) => data)).toStrictEqual([ - 'c1', - 'c2', - 'c3', - 'c4', - 'c5', - 'c6', - 'c7', - 'c8', - 'c9', - 'f1', - 'c10', - 'c11', - 'c12', - 'f2', - 'f3', - 'f4', - ]); - }); -}); diff --git a/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts b/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts deleted file mode 100644 index e842510515..0000000000 --- a/src/web3/adl/feed-crdt/__tests__/Feed.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {FeedFactory} from '../FeedFactory'; -import {FeedFrameDto} from '../types'; - -const setup = () => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const feeds = new FeedFactory({hlcs, cas}); - return { - hlcs, - cas0, - cas, - feeds, - }; -}; - -test('can instantiate factory', async () => { - setup(); -}); - -test('can create a new empty feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - expect(feed.getSnapshot()).toStrictEqual([]); -}); - -test('can persist empty feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - await feed.save(); - expect(feed.getSnapshot()).toStrictEqual([]); -}); - -test('can add entries to a new feed', async () => { - const {feeds} = setup(); - const feed = feeds.make(); - expect(feed.getSnapshot().length).toStrictEqual(0); - feed.add('asdf'); - expect(feed.getSnapshot().length).toStrictEqual(1); - expect(feed.getSnapshot()[0][2]).toStrictEqual('asdf'); - feed.add(['abc']); - expect(feed.getSnapshot().length).toStrictEqual(2); - expect(feed.getSnapshot()[1][2]).toStrictEqual(['abc']); -}); - -test('can save and restore a feed', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.add('asdf'); - feed1.add('abc'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res).toBeInstanceOf(Array); - expect(res.length).toBe(3); - expect(res[0]).toBe(null); - expect(res[1]).toBe(0); - expect(res[2]).toBeInstanceOf(Array); - const feed2 = await feeds.load(cid); - expect(feed2.getSnapshot().length).toStrictEqual(2); - expect(feed2.getSnapshot()[0][2]).toStrictEqual('asdf'); - expect(feed2.getSnapshot()[1][2]).toStrictEqual('abc'); -}); - -test('can delete an entry', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - const id1 = feed1.add('a1'); - const id2 = feed1.add('a2'); - const id3 = feed1.add('a3'); - expect(feed1.getSnapshot().length).toStrictEqual(3); - const cid1 = await feed1.save(); - expect(feed1.getSnapshot().length).toStrictEqual(3); - feed1.del(id1); - expect(feed1.getSnapshot().length).toStrictEqual(2); - expect(feed1.getSnapshot()[0][2]).toStrictEqual('a2'); - expect(feed1.getSnapshot()[1][2]).toStrictEqual('a3'); - const cid2 = await feed1.save(); - const res = (await cas.get(cid2)) as FeedFrameDto; - expect(res[2].length).toStrictEqual(4); - expect(cid2.is(cid1)).toBe(false); - const feed2 = await feeds.load(cid2); - expect(feed2.getSnapshot().length).toStrictEqual(2); - feed2.del(id3); - expect(feed2.getSnapshot().length).toStrictEqual(1); - const cid3 = await feed2.save(); - const feed3 = await feeds.load(cid3); - expect(feed3.getSnapshot().length).toStrictEqual(1); - expect(feed1.getSnapshot()[0][2]).toStrictEqual('a2'); - feed3.del(id2); - expect(feed3.getSnapshot().length).toStrictEqual(0); - const cid4 = await feed3.save(); - const feed4 = feeds.make(); - const cidEmpty = await feed4.save(); - expect(cid4.is(cidEmpty)).toBe(false); -}); - -test('delete items from another frame', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.opsPerFrame = 5; - const id1 = feed1.add('a1'); - const id2 = feed1.add('a2'); - const id3 = feed1.add('a3'); - const id4 = feed1.add('a4'); - const id5 = feed1.add('a5'); - const id6 = feed1.add('a6'); - const id7 = feed1.add('a7'); - const id8 = feed1.add('a8'); - const cid1 = await feed1.save(); - const id9 = feed1.add('a9'); - const id10 = feed1.add('a10'); - const id11 = feed1.add('a11'); - const id12 = feed1.add('a12'); - const id13 = feed1.add('a13'); - const id14 = feed1.add('a14'); - const cid2 = await feed1.save(); - const frame = (await cas.get(cid2)) as FeedFrameDto; - expect(frame[1]).toBe(1); - expect(feed1.getSnapshot()[1][2]).toBe('a2'); - await feed1.del(id2); - expect(feed1.getSnapshot()[1][2]).toBe('a3'); - const cid3 = await feed1.save(); - const feed2 = await feeds.load(cid3); - await feed2.loadAll(); - expect(feed2.getSnapshot()[1][2]).toBe('a3'); -}); - -test('combines entries in one block, if operation count is withing a threshold', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - feed1.add('entry-1'); - feed1.add('entry-2'); - await feed1.save(); - feed1.add('entry-3'); - feed1.add('entry-4'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res[2].length).toBe(4); -}); - -test('creates a new block if last one has 25 or more entries', async () => { - const {feeds, cas} = setup(); - const feed1 = feeds.make(); - for (let i = 0; i < feed1.opsPerFrame; i++) { - feed1.add('entry-' + i); - } - await feed1.save(); - feed1.add('entry-x'); - feed1.add('entry-y'); - const cid = await feed1.save(); - const res = (await cas.get(cid)) as FeedFrameDto; - expect(res[2].length).toBe(2); -}); - -test('can create and read out 200 entries', async () => { - const {feeds} = setup(); - const feed1 = feeds.make(); - for (let i = 0; i < 200; i++) { - feed1.add('entry-' + i); - await feed1.save(); - } - const cid = feed1.cid()!; - const feed2 = await feeds.load(cid); - await feed2.loadAll(); - const entries = feed2.getSnapshot(); - expect(entries.length).toBe(200); - for (let i = 0; i < 200; i++) { - expect(entries[i][2]).toBe('entry-' + i); - } -}); - -test('view shows only entries, which are not deleted', async () => { - const {hlcs, cas, feeds} = setup(); - const feed1 = feeds.make(); - feed1.add('a'); - const bId = feed1.add('b'); - feed1.add('c1'); - feed1.add('c2'); - feed1.add('c3'); - feed1.add('c4'); - feed1.add('c5'); - feed1.add('c6'); - feed1.add('c7'); - feed1.add('c8'); - feed1.add('c9'); - feed1.add('c10'); - feed1.add('c11'); - await feed1.save(); - feed1.del(bId); - const cid = await feed1.save(); - const feeds2 = new FeedFactory({hlcs, cas}); - const feed2 = await feeds2.load(cid); - await feed2.loadAll(); - const list = feed2.getSnapshot(); - expect(list[0][2]).toBe('a'); - expect(list[1][2]).toBe('c1'); - expect(list[2][2]).toBe('c2'); - expect(list[3][2]).toBe('c3'); - expect(list[4][2]).toBe('c4'); - expect(list[5][2]).toBe('c5'); - expect(list[6][2]).toBe('c6'); - expect(list[7][2]).toBe('c7'); - expect(list[8][2]).toBe('c8'); - expect(list[9][2]).toBe('c9'); - expect(list[10][2]).toBe('c10'); - expect(list[11][2]).toBe('c11'); -}); diff --git a/src/web3/adl/feed-crdt/constants.ts b/src/web3/adl/feed-crdt/constants.ts deleted file mode 100644 index a648e09ae8..0000000000 --- a/src/web3/adl/feed-crdt/constants.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const enum FeedOpType { - Insert = 0, - Delete = 1, -} - -export const enum FeedConstraints { - /** - * Number of ops per frame that triggers a new frame creation. - */ - DefaultOpsPerFrameThreshold = 25, - - FirstFrameSeq = 0, -} diff --git a/src/web3/adl/feed-crdt/types.ts b/src/web3/adl/feed-crdt/types.ts deleted file mode 100644 index 0d0c7fd8ae..0000000000 --- a/src/web3/adl/feed-crdt/types.ts +++ /dev/null @@ -1,37 +0,0 @@ -import type {FeedOpType} from './constants'; -import type {HlcDto} from '../../hlc'; -import type {Cid} from '../../multiformats'; - -/** - * Public API of a feed CRDT. - */ -export interface FeedApi { - /** Append a new item to the end of the feed. */ - add(data: unknown): HlcDto; - /** Delete some item from the feed. */ - del(operationId: HlcDto): void; - /** Load the latest entries of the feed. */ - loadHead(cid: Cid): Promise; - /** Load more entries of the feed. */ - loadMore(): Promise; - /** Whether feed has more frames to load. */ - hasMore(): boolean; - /** Persist any unsaved changes to the storage. */ - save(): Promise; -} - -/** - * Serialized feed frame. - */ -export type FeedFrameDto = [ - /** CID of the previous block. */ - prev: Uint8Array | null, - /** Monotonically incrementing sequence number for each new block. */ - seq: number, - /** A list of operations that current block contains. */ - ops: FeedOp[], -]; - -export type FeedOpInsert = [type: FeedOpType.Insert, id: HlcDto, value: unknown]; -export type FeedOpDelete = [type: FeedOpType.Delete, id: HlcDto]; -export type FeedOp = FeedOpInsert | FeedOpDelete; diff --git a/src/web3/adl/hamt-crdt/Hamt.ts b/src/web3/adl/hamt-crdt/Hamt.ts deleted file mode 100644 index 8d5edc957d..0000000000 --- a/src/web3/adl/hamt-crdt/Hamt.ts +++ /dev/null @@ -1,106 +0,0 @@ -import {Defer} from 'thingies/es2020/Defer'; -import {concurrency} from 'thingies/es2020/concurrencyDecorator'; -import {HamtFrame} from './HamtFrame'; -import * as hlc from '../../hlc'; -import {Cid} from '../../multiformats'; -import {sha256} from '../../crypto'; -import {toBuf} from '@jsonjoy.com/util/lib/buffers/toBuf'; -import type {CidCasStruct} from '../../store/cas/CidCasStruct'; -import type * as types from './types'; - -export interface HamtDependencies { - cas: CidCasStruct; - hlcs: hlc.HlcFactory; -} - -export class Hamt implements types.HamtApi { - protected _root: HamtFrame; - protected _dirty: boolean = false; - protected _loading: Promise | null = null; - - public cid: Cid | null = null; - public prev: Cid | null = null; - public seq: number = 0; - public ops: types.HamtOp[] = []; - - constructor(protected readonly deps: HamtDependencies) { - this._root = new HamtFrame(deps.cas, null); - } - - public hasChanges(): boolean { - return this._dirty; - } - - // ------------------------------------------------------------------ HamtApi - - public async load(cid: Cid): Promise { - this.cid = cid; - const future = new Defer(); - this._loading = future.promise; - try { - const [prev, seq, ops, data] = (await this.deps.cas.get(cid)) as types.HamtRootFrameDto; - this.prev = prev; - this.seq = seq; - this._root.loadDto(data, null); - future.resolve(); - } catch (err) { - future.reject(err); - } finally { - this._loading = null; - } - return future.promise; - } - - @concurrency(1) - public async put(key: Uint8Array | string, val: unknown): Promise { - if (this._loading) await this._loading; - const hashedKey = await this._key(key); - const id = this.deps.hlcs.inc(); - const idDto = hlc.toDto(id); - const op: types.HamtOp = [hashedKey, val, idDto]; - const success = await this._root.put(op); - if (success) this.ops.push(op); - return success; - } - - public async get(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - const hashedKey = await this._key(key); - return await this._root.get(hashedKey); - } - - /** Convert any key to buffer and prefix with 4-byte hash. */ - protected async _key(key: Uint8Array | string): Promise { - const keyBuf = typeof key === 'string' ? toBuf(key) : key; - const hash = await sha256(keyBuf); - const buf = new Uint8Array(4 + keyBuf.length); - buf.set(hash.subarray(0, 4), 0); - buf.set(keyBuf, 4); - return buf; - } - - public async has(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - return (await this.get(key)) !== undefined; - } - - public async del(key: Uint8Array | string): Promise { - if (this._loading) await this._loading; - return await this.put(key, undefined); - } - - public async save(): Promise<[head: Cid, affected: Cid[]]> { - const [, affected] = await this._root.saveChildren(); - const prev = this.cid; - const seq = this.seq + 1; - const frameDto = this._root.toDto(); - const dto: types.HamtRootFrameDto = [prev, seq, this.ops, frameDto]; - const cid = await this.deps.cas.put(dto); - this.cid = cid; - this.prev = prev; - this.seq = seq; - this.ops = []; - affected.push(cid); - return [cid, affected]; - } -} diff --git a/src/web3/adl/hamt-crdt/HamtFactory.ts b/src/web3/adl/hamt-crdt/HamtFactory.ts deleted file mode 100644 index 15397c35f6..0000000000 --- a/src/web3/adl/hamt-crdt/HamtFactory.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {Hamt, type HamtDependencies} from './Hamt'; - -export interface HamtFactoryDependencies extends HamtDependencies {} - -export class HamtFactory { - constructor(protected readonly deps: HamtFactoryDependencies) {} - - public make(): Hamt { - const hamt = new Hamt(this.deps); - return hamt; - } -} diff --git a/src/web3/adl/hamt-crdt/HamtFrame.ts b/src/web3/adl/hamt-crdt/HamtFrame.ts deleted file mode 100644 index 14b62e32c0..0000000000 --- a/src/web3/adl/hamt-crdt/HamtFrame.ts +++ /dev/null @@ -1,217 +0,0 @@ -import {Defer} from 'thingies/es2020/Defer'; -import {cmpUint8Array2} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array2'; -import {cmpDto} from '../../hlc'; -import {CidCasStruct} from '../../store/cas/CidCasStruct'; -import {Cid} from '../../multiformats'; -import {HamtConstraints} from './constants'; -import {mutex} from 'thingies/es2020/mutex'; -import type * as types from './types'; - -export class HamtFrame { - protected _entries: types.HamtFrameEntry[] = []; - protected _children: (HamtFrame | null)[] = [null]; - protected _loaded: boolean = false; - protected _loading: Defer | null = null; - /** Maybe instead of `_dirty`, just consider `id` === `null` to mean there are unsaved changes. */ - protected _dirty: boolean = false; - - constructor( - protected readonly cas: CidCasStruct, - public cid: Cid | null, - ) {} - - protected async ensureLoaded(): Promise { - if (this._loading) return this._loading.promise; - if (this._loaded) return; - if (!this.cid) return; - if (this.cid) await this.load(); - } - - /** - * Load the current node by CID from CAS. - * - * @param id CID of the node to load. - */ - @mutex - protected async load(): Promise { - const id = this.cid; - if (!id) throw new Error('ID_NOT_SET'); - this._loading = new Defer(); - const data = (await this.cas.get(id)) as types.HamtFrameDto; - this.loadDto(data, id); - this._loading.resolve(); - this._loading = null; - } - - /** - * Load the current node from known data. Provided data will be mutated - * internally, so it MUST not be used after this method is called. - * - * @param data Serialized data of the node to load. - * @param cid CID of the node to load, or null if CID is not known. - */ - public loadDto(data: types.HamtFrameDto, cid: Cid | null) { - this.cid = cid; - const [entries, children] = data; - this._entries = entries; - this._children = []; - const length = children.length; - for (let i = 0; i < length; i++) { - const childCid = children[i]; - const child = childCid ? new HamtFrame(this.cas, childCid) : null; - this._children.push(child); - } - this._loaded = true; - } - - public async getEntry(key: Uint8Array): Promise { - if (!this._loaded) await this.ensureLoaded(); - const entries = this._entries; - const length = entries.length; - for (let i = 0; i < length; i++) { - const entry = entries[i]; - const currentKey = entry[0]; - const comparison = cmpUint8Array2(currentKey, key); - if (comparison === 0) return entry; - const isKeySmallerThanCurrentKey = comparison > 0; - if (isKeySmallerThanCurrentKey) { - const child = this._children[i]; - if (!child) return undefined; - return await child.getEntry(key); - } - } - const lastChild = this._children[length]; - if (!lastChild) return undefined; - return await lastChild.getEntry(key); - } - - /** - * Recursively find a key value from current node or any of its children. - * - * @param key The key to fetch. - * @returns Returns the value if found, otherwise undefined. - */ - public async get(key: Uint8Array): Promise { - const entry = await this.getEntry(key); - if (!entry) return undefined; - return entry[1]; - } - - public async has(key: Uint8Array): Promise { - return (await this.get(key)) !== undefined; - } - - /** - * Insert or overwrite a key value pair in current node or any of its children. - * - * @param id HLC ID of the key. - * @param key Key to put. - * @param val Key value to put. - * @returns Returns true if the key was inserted. Insertion can fail if the - * ID of the insert operation is lower than the ID of the last write. - */ - public async put(op: types.HamtOp): Promise { - if (!this._loaded) await this.ensureLoaded(); - const [key, , id] = op; - const entries = this._entries; - const length = entries.length; - const insertInChild = length >= HamtConstraints.MaxEntriesPerFrame; - for (let i = 0; i < length; i++) { - const entry = entries[i]; - const currentKey = entry[0]; - const comparison = cmpUint8Array2(currentKey, key); - // Replace existing entry if keys are equal. - if (comparison === 0) { - const oldId = entry[2]; - if (cmpDto(oldId, id) >= 0) return false; - this._entries[i] = op; - this._markDirty(); - return true; - } - const isKeySmallerThanCurrentKey = comparison > 0; - if (isKeySmallerThanCurrentKey) { - if (insertInChild) { - // Insert at child node. - const wasInserted = await this._putAtChild(i, op); - if (wasInserted) this._markDirty(); - return wasInserted; - } else { - // Insert at current node, but shifting entries to the right. - this._entries.splice(i, 0, op); - this._children.splice(i, 0, null); - this._markDirty(); - return true; - } - } - } - // Insert into the last child. - if (insertInChild) { - const wasInserted = await this._putAtChild(length, op); - if (wasInserted) this._markDirty(); - return wasInserted; - } - // Append entry at the end of current block. - this._entries.push(op); - this._children.push(null); - this._markDirty(); - return true; - } - - protected _markDirty() { - this._dirty = true; - this.cid = null; - } - - private async _putAtChild(i: number, op: types.HamtOp): Promise { - let child = this._children[i]; - if (!child) child = this._children[i] = new HamtFrame(this.cas, null); - return await child.put(op); - } - - /** - * Save current node and all of its children. - * - * @returns Returns CID of current node, and a list of all affected CIDs, - * including the current CID. - */ - public async save(): Promise<[id: Cid, affected: Cid[]]> { - if (!this._loaded) await this.ensureLoaded(); - // TODO: Maybe throw if there are no changes. - if (this.cid && !this._dirty) return [this.cid, []]; - const [children, affected] = await this.saveChildren(); - const data: types.HamtFrameDto = [this._entries, children]; - const cid = await this.cas.put(data); - this.cid = cid; - affected.push(cid); - return [cid, affected]; - } - - /** - * Saves all "dirty" children and returns a list of all children. - * - * @returns Returns a list of stored CIDs and a all children of the current node, - * even the children which were not saved. - */ - public async saveChildren(): Promise<[children: (Cid | null)[], affected: Cid[]]> { - const ids: Cid[] = []; - const children: (Cid | null)[] = []; - const length = this._children.length; - for (let i = 0; i < length; i++) { - const child = this._children[i]; - if (!child) { - children.push(null); - continue; - } - const [childCid, affected] = await child.save(); - ids.push(...affected); - children.push(childCid); - } - return [children, ids]; - } - - public toDto(): types.HamtFrameDto { - const children = this._children.map((child) => (child ? child.cid : null)); - const dto: types.HamtFrameDto = [this._entries, children]; - return dto; - } -} diff --git a/src/web3/adl/hamt-crdt/README.md b/src/web3/adl/hamt-crdt/README.md deleted file mode 100644 index eb4d07b59b..0000000000 --- a/src/web3/adl/hamt-crdt/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# HAMT CRDT - -HAMT CRDT is an infinitely scalable key-value store that is implemented using as -a HAMT data structure as a CRDT. Each HAMT node stores up to 16 *key-value-time* -3-tuple entries and up to 17 links to child nodes. - -It supports only a single `.put()` operation. To delete a key, one puts an -`undefined` value. Each operation is accompanied by an ID (timestamp), expressed -as hybrid logical clock (HLC) value. Each key is a single value LWW register, -where the HLC clock is used for determining the winner. diff --git a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts b/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts deleted file mode 100644 index ceece93f88..0000000000 --- a/src/web3/adl/hamt-crdt/__tests__/Hamt.spec.ts +++ /dev/null @@ -1,265 +0,0 @@ -import {b} from '@jsonjoy.com/util/lib/buffers/b'; -import {HlcFactory} from '../../../hlc'; -import {CidCasMemory} from '../../../store/cas/CidCasMemory'; -import {CidCasStructCbor} from '../../../store/cas/CidCasStructCbor'; -import {HamtFactory} from '../HamtFactory'; -import {HamtRootFrameDto} from '../types'; - -const setup = () => { - const hlcs = new HlcFactory({}); - const cas0 = new CidCasMemory(); - const cas = new CidCasStructCbor(cas0); - const hamts = new HamtFactory({hlcs, cas}); - const hamt = hamts.make(); - return { - hlcs, - cas0, - cas, - hamts, - hamt, - }; -}; - -const toArr = (buf: Uint8Array): number[] => { - const arr: number[] = []; - for (let i = 0; i < buf.length; i++) arr.push(buf[i]); - return arr; -}; - -describe('HamtCrdt', () => { - test('new database has no changes', async () => { - const {hamt} = setup(); - const res = hamt.hasChanges(); - expect(res).toBe(false); - }); - - describe('.get()', () => { - test('returns undefined in empty database', async () => { - const {hamt} = setup(); - const res1 = await hamt.get(b(1, 2, 3)); - const res2 = await hamt.get('test'); - expect(res1).toBe(undefined); - expect(res2).toBe(undefined); - }); - - test('returns undefined in empty database', async () => { - const {hamt} = setup(); - const res1 = await hamt.get(b(1, 2, 3)); - const res2 = await hamt.get('test'); - expect(res1).toBe(undefined); - expect(res2).toBe(undefined); - }); - }); - - describe('.put()', () => { - test('can store a string key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put('test', b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get('test'); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can store a multiple keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.put('/@user1', b(1, 2, 3)); - const res2 = await hamt.put('/@user2', b(4, 5, 6)); - const res3 = await hamt.put('/@user3', b(7, 7, 7)); - expect(res1).toBe(true); - expect(res2).toBe(true); - expect(res3).toBe(true); - const res4 = await hamt.get('/@user1'); - const res5 = await hamt.get('/@user2'); - const res6 = await hamt.get('/@user3'); - expect(res4).toStrictEqual(b(1, 2, 3)); - expect(res5).toStrictEqual(b(4, 5, 6)); - expect(res6).toStrictEqual(b(7, 7, 7)); - }); - - test('can store into a binary key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(69), b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get(b(69)); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can store into an empty key', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(), b(1, 2, 3)); - expect(res1).toBe(true); - const res2 = await hamt.get(b()); - expect(res2).toStrictEqual(b(1, 2, 3)); - }); - - test('can overwrite a key', async () => { - const {hamt} = setup(); - await hamt.put('foo', b(1, 2, 3)); - await hamt.put('foo', b(4, 5, 6)); - const res2 = await hamt.get('foo'); - expect(res2).toStrictEqual(b(4, 5, 6)); - }); - - test('can add more than 16 keys', async () => { - const {hamt} = setup(); - for (let i = 0; i < 30; i++) { - await hamt.put('foo-' + i, b(i)); - } - for (let i = 0; i < 30; i++) { - const res = await hamt.get('foo-' + i); - expect(res).toStrictEqual(b(i)); - } - }); - - test('can store any serializable value', async () => { - const {hamt} = setup(); - const res1 = await hamt.put(b(), {foo: 123, bar: [true, false]}); - expect(res1).toBe(true); - const res2 = await hamt.get(b()); - expect(res2).toStrictEqual({foo: 123, bar: [true, false]}); - }); - }); - - describe('.has()', () => { - test('returns false for missing keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.has('a'); - const res2 = await hamt.has('b'); - expect(res1).toBe(false); - expect(res2).toBe(false); - }); - - test('returns true for existing keys', async () => { - const {hamt} = setup(); - await hamt.put('a', b()); - await hamt.put('b', b(1, 2, 3)); - const res1 = await hamt.has('a'); - const res2 = await hamt.has('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - }); - }); - - describe('.del()', () => { - test('can delete non-existing keys', async () => { - const {hamt} = setup(); - const res1 = await hamt.del('a'); - const res2 = await hamt.del('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - const res3 = await hamt.get('a'); - const res4 = await hamt.get('b'); - expect(res3).toBe(undefined); - expect(res4).toBe(undefined); - }); - - test('can delete existing key', async () => { - const {hamt} = setup(); - await hamt.put('a', b()); - await hamt.put('b', b(1, 2, 3)); - const res1 = await hamt.del('a'); - const res2 = await hamt.del('b'); - expect(res1).toBe(true); - expect(res2).toBe(true); - const res3 = await hamt.get('a'); - const res4 = await hamt.get('b'); - expect(res3).toBe(undefined); - expect(res4).toBe(undefined); - }); - }); - - describe('.save()', () => { - test('can persist empty HAMT', async () => { - const {hamt, hamts} = setup(); - const [cid] = await hamt.save(); - expect(cid).toBeDefined(); - const hamt2 = hamts.make(); - await hamt2.load(cid); - }); - - test('can save a single key', async () => { - const {hamt, cas0} = setup(); - const data = 111; - await hamt.put('a', b(data)); - const size = await (cas0 as CidCasMemory).size(); - expect(size).toBe(0); - const [cid] = await hamt.save(); - expect(await (cas0 as CidCasMemory).size()).toBe(size + 1); - const blob = await cas0.get(cid); - const found = toArr(blob).findIndex((octet) => octet === data); - expect(found > -1).toBe(true); - }); - - test('can load saved data', async () => { - const {hamt, hamts} = setup(); - await hamt.put('a', b(123)); - const [cid] = await hamt.save(); - const hamt2 = hamts.make(); - const res1 = await hamt2.get('a'); - expect(res1).toBe(undefined); - await hamt2.load(cid); - const res2 = await hamt2.get('a'); - expect(res2).toStrictEqual(b(123)); - }); - - test('can save and load more than 16 keys of data', async () => { - const {hamt, hamts} = setup(); - const keys = 1111; - for (let i = 0; i < keys; i++) { - await hamt.put('a:' + i, b(i, i + 1, i + 2)); - } - const [cid, all] = await hamt.save(); - const hamt2 = hamts.make(); - await hamt2.load(cid); - for (let i = 0; i < keys; i++) { - const res = await hamt2.get('a:' + i); - expect(res).toStrictEqual(b(i, i + 1, i + 2)); - } - }); - - test('can save and load more than 16 keys .save()"ed at periodic intervals', async () => { - const {hamt, hamts} = setup(); - const keysPerSave = 10; - const saves = 20; - for (let j = 0; j < saves; j++) { - for (let i = 0; i < keysPerSave; i++) { - const key = j * keysPerSave + i; - await hamt.put('abc:' + key, b(key, key + 1, key + 2)); - } - await hamt.save(); - } - const hamt2 = hamts.make(); - await hamt2.load(hamt.cid!); - for (let j = 0; j < saves; j++) { - for (let i = 0; i < keysPerSave; i++) { - const key = j * keysPerSave + i; - const res = await hamt2.get('abc:' + key); - expect(res).toStrictEqual(b(key, key + 1, key + 2)); - } - } - }); - }); - - describe('operations', () => { - test('stores operations as keys are edited', async () => { - const {hamt} = setup(); - expect(hamt.ops.length).toBe(0); - await hamt.put(b(0), 0); - expect(hamt.ops.length).toBe(1); - expect(hamt.ops).toStrictEqual([[expect.any(Uint8Array), 0, expect.any(Array)]]); - await hamt.put(b(1), 1); - expect(hamt.ops.length).toBe(2); - expect(hamt.ops).toStrictEqual([ - [expect.any(Uint8Array), 0, expect.any(Array)], - [expect.any(Uint8Array), 1, expect.any(Array)], - ]); - await hamt.del(b(0)); - expect(hamt.ops.length).toBe(3); - expect(hamt.ops).toStrictEqual([ - [expect.any(Uint8Array), 0, expect.any(Array)], - [expect.any(Uint8Array), 1, expect.any(Array)], - [hamt.ops[0][0], undefined, expect.any(Array)], - ]); - }); - }); -}); diff --git a/src/web3/adl/hamt-crdt/constants.ts b/src/web3/adl/hamt-crdt/constants.ts deleted file mode 100644 index c12b921b13..0000000000 --- a/src/web3/adl/hamt-crdt/constants.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const enum HamtConstraints { - MaxEntriesPerFrame = 16, -} diff --git a/src/web3/adl/hamt-crdt/types.ts b/src/web3/adl/hamt-crdt/types.ts deleted file mode 100644 index ee9e54d8d8..0000000000 --- a/src/web3/adl/hamt-crdt/types.ts +++ /dev/null @@ -1,64 +0,0 @@ -import type {HlcDto} from '../../hlc'; -import type {Cid} from '../../multiformats'; - -export interface HamtApi { - load(id: Cid): Promise; - put(key: Uint8Array | string, val: unknown): Promise; - get(key: Uint8Array | string): Promise; - has(key: Uint8Array | string): Promise; - del(key: Uint8Array | string): Promise; - save(): Promise<[head: Cid, affected: Cid[]]>; -} - -/** Data of the root node of the HAMT. */ -export type HamtRootFrameDto = [ - /** - * CID of the previous state, previous root node. Zero, if there is no - * previous state. - */ - prev: Cid | null, - - /** - * Monotonically incrementing sequence number of the current state - * (increments with each new state). - */ - seq: number, - - /** - * An ordered list of operations which were performed on previous state to - * create the current state. Sorted, where the first operation is the oldest. - */ - ops: HamtOp[], - - /** - * Root level data of the HAMT. - */ - data: HamtFrameDto, -]; - -export type HamtFrameDto = [ - /** - * List of key value pairs stored in this node. - */ - entries: HamtFrameEntry[], - - /** - * Links to child nodes. This array must always be exactly one element larger - * than the `entries` array. Gaps are filled with nulls. - */ - children: (Cid | null)[], -]; - -export type HamtFrameEntry = [key: Uint8Array, val: unknown, id: HlcDto]; - -/** - * Key update operation. - */ -export type HamtOp = [ - /** Key that was updated. */ - key: Uint8Array, - /** New value of the key. */ - val: unknown, - /** ID of the operation as hybrid logical clock. */ - id: HlcDto, -]; diff --git a/src/web3/codec/Codecs.ts b/src/web3/codec/Codecs.ts deleted file mode 100644 index 7229348474..0000000000 --- a/src/web3/codec/Codecs.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {MulticodecIpld} from '../multiformats'; -import type {IpldCodec} from './types'; - -export class Codecs { - protected readonly map = new Map(); - - public set(codec: MulticodecIpld, jsonCodec: IpldCodec): void { - this.map.set(codec, jsonCodec); - } - - public get(codec: MulticodecIpld): IpldCodec | undefined { - return this.map.get(codec); - } - - public getOrThrow(codec: MulticodecIpld): IpldCodec { - const jsonCodec = this.get(codec); - if (!jsonCodec) throw new Error(`Codec ${codec} (0x${codec.toString(16)}) not found`); - return jsonCodec; - } -} diff --git a/src/web3/codec/codecs/__tests__/cbor.spec.ts b/src/web3/codec/codecs/__tests__/cbor.spec.ts deleted file mode 100644 index bcfc278429..0000000000 --- a/src/web3/codec/codecs/__tests__/cbor.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {Cid} from '../../../multiformats'; -import {cbor} from '../cbor'; - -test('can encode and decode CID', async () => { - const cid = await Cid.fromData(new Uint8Array([1, 2, 3, 4])); - const data = {foo: cid}; - const encoded = cbor.encoder.encode(data); - const decoded = cbor.decoder.decode(encoded); - expect(decoded).toStrictEqual(data); -}); - -test('can encode simplest fixture', async () => { - const data = [2]; - const encoded = cbor.encoder.encode(data); - const decoded = cbor.decoder.decode(encoded); - expect(decoded).toStrictEqual(data); - expect(encoded.length).toBe(2); - expect(encoded[0]).toBe(0x81); - expect(encoded[1]).toBe(0x02); - const cid = await Cid.fromDagCbor(encoded); - expect(cid.toText('base32')).toBe('bafyreihdb57fdysx5h35urvxz64ros7zvywshber7id6t6c6fek37jgyfe'); -}); diff --git a/src/web3/codec/codecs/cbor.ts b/src/web3/codec/codecs/cbor.ts deleted file mode 100644 index 96f18fc171..0000000000 --- a/src/web3/codec/codecs/cbor.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {CborEncoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborEncoderDag'; -import {CborDecoderDag} from '@jsonjoy.com/json-pack/lib/cbor/CborDecoderDag'; -import {Cid} from '../../multiformats'; -import {writer} from './writer'; -import type {IpldCodec} from '../types'; - -const encoder = new (class extends CborEncoderDag { - public writeUnknown(val: unknown): void { - if (val instanceof Cid) this.writeTag(42, val.toBinary()); - else throw new Error('UNKNOWN_VALUE'); - } -})(writer); - -const decoder = new (class extends CborDecoderDag { - public readTagRaw(tag: number): Cid | unknown { - const value = this.val(); - if (tag === 42) return Cid.fromBinary(value as Uint8Array); - throw new Error('UNKNOWN_TAG'); - } -})(); - -export const cbor: IpldCodec = { - name: 'DAG-CBOR', - encoder, - decoder, -}; diff --git a/src/web3/codec/codecs/raw.ts b/src/web3/codec/codecs/raw.ts deleted file mode 100644 index 18101aa0bc..0000000000 --- a/src/web3/codec/codecs/raw.ts +++ /dev/null @@ -1,19 +0,0 @@ -// import {bufferToUint8Array} from '../../../util/buffers/bufferToUint8Array'; -import type {IpldCodec} from '../types'; - -export const raw: IpldCodec = { - name: 'Raw', - encoder: { - encode: (value: unknown): Uint8Array => { - if (value instanceof Uint8Array) return value; - // if (typeof Buffer !== 'undefined') { - // if(Buffer.isBuffer(value)) return bufferToUint8Array(value as Buffer); - // return bufferToUint8Array(Buffer.from(String(value))); - // } - throw new Error('VALUE_NOT_SUPPORTED'); - }, - }, - decoder: { - decode: (data: Uint8Array): unknown => data, - }, -}; diff --git a/src/web3/codec/codecs/writer.ts b/src/web3/codec/codecs/writer.ts deleted file mode 100644 index 8636e0f104..0000000000 --- a/src/web3/codec/codecs/writer.ts +++ /dev/null @@ -1,3 +0,0 @@ -import {Writer} from '@jsonjoy.com/util/lib/buffers/Writer'; - -export const writer = new Writer(); diff --git a/src/web3/codec/index.ts b/src/web3/codec/index.ts deleted file mode 100644 index 77f5672358..0000000000 --- a/src/web3/codec/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {MulticodecIpld} from '../multiformats'; -import {Codecs} from './Codecs'; -import {raw} from './codecs/raw'; -import {cbor} from './codecs/cbor'; - -export * from './types'; -export * from './Codecs'; - -export const codecs = new Codecs(); - -codecs.set(MulticodecIpld.Raw, raw); -codecs.set(MulticodecIpld.Cbor, cbor); -codecs.set(MulticodecIpld.DagCbor, cbor); diff --git a/src/web3/codec/types.ts b/src/web3/codec/types.ts deleted file mode 100644 index 8c72eaf954..0000000000 --- a/src/web3/codec/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type {BinaryJsonDecoder, BinaryJsonEncoder} from '@jsonjoy.com/json-pack/lib/types'; - -export interface IpldCodec { - name: string; - encoder: Pick; - decoder: Pick; -} diff --git a/src/web3/constants.ts b/src/web3/constants.ts deleted file mode 100644 index 4b4dd07c1f..0000000000 --- a/src/web3/constants.ts +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Whether this code runs in a Node.js process. - */ -export const isNode: boolean = !!process?.versions?.node; diff --git a/src/web3/crypto/index.ts b/src/web3/crypto/index.ts deleted file mode 100644 index c7c41c22e3..0000000000 --- a/src/web3/crypto/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './webcrypto'; -export * from './sha256'; diff --git a/src/web3/crypto/sha256.ts b/src/web3/crypto/sha256.ts deleted file mode 100644 index 630abd2cb4..0000000000 --- a/src/web3/crypto/sha256.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {crypto} from './webcrypto'; - -export const sha256 = async (buf: Uint8Array): Promise => { - const ab = await crypto.subtle.digest('SHA-256', buf); - return new Uint8Array(ab); -}; diff --git a/src/web3/crypto/webcrypto.ts b/src/web3/crypto/webcrypto.ts deleted file mode 100644 index 51f41f6428..0000000000 --- a/src/web3/crypto/webcrypto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {isNode} from '../constants'; - -/** - * Universal Node.js/browser Web Crypto API reference. - * - * @todo Maybe create an isomorphic package for this? - */ -export const crypto: Crypto = isNode ? require('node:crypto').webcrypto : window.crypto; diff --git a/src/web3/hlc/Hlc.ts b/src/web3/hlc/Hlc.ts deleted file mode 100644 index 1b767a4ada..0000000000 --- a/src/web3/hlc/Hlc.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type {HybridLogicalClock} from './types'; - -/** - * @todo Rename this such that it includes "timestamp" in the name. - */ -export class Hlc implements HybridLogicalClock { - public readonly ts: number; - public readonly seq: number; - public readonly node: number; - - constructor(ts: number, seq: number, node: number) { - this.ts = ts; - this.seq = seq; - this.node = node; - } -} diff --git a/src/web3/hlc/HlcFactory.ts b/src/web3/hlc/HlcFactory.ts deleted file mode 100644 index ccc508dbf9..0000000000 --- a/src/web3/hlc/HlcFactory.ts +++ /dev/null @@ -1,37 +0,0 @@ -import {create, inc} from './util'; -import {Hlc} from './Hlc'; - -export interface HlcFactoryDependencies { - /** Wall clock generator. */ - now?: () => number; - - /** ID of the current node/process. */ - node?: number; -} - -const defaultNow = () => Math.round(Date.now() / 1000) - 2272147200; - -export class HlcFactory { - public id: Hlc; - public readonly now: () => number; - public readonly node: number; - - constructor({now = defaultNow, node = Math.round(Math.random() * 0xff)}: HlcFactoryDependencies) { - this.now = now; - this.node = node; - this.id = new Hlc(now(), 0, node); - } - - public create(): Hlc { - return create(this.now(), this.node); - } - - /** - * @todo Is this method necessary? - */ - public inc(): Hlc { - const id = inc(this.id, this.now()); - this.id = id; - return id; - } -} diff --git a/src/web3/hlc/README.md b/src/web3/hlc/README.md deleted file mode 100644 index be09558e51..0000000000 --- a/src/web3/hlc/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Hybrid Logic Clock - -Hybrid logical clock is a 3-tuple of: (1) wall clock; (2) logical sequence -number; and (3) a node session ID. The wall clock is the current time in -seconds or milliseconds. The logical sequence number is a monotonically -increasing counter that is incremented whenever wall clocks are equal. The node -session ID is a unique identifier for the node generating the HLC. diff --git a/src/web3/hlc/__tests__/HlcFactory.spec.ts b/src/web3/hlc/__tests__/HlcFactory.spec.ts deleted file mode 100644 index 87b3f5885c..0000000000 --- a/src/web3/hlc/__tests__/HlcFactory.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {HlcFactory, HlcFactoryDependencies} from '../HlcFactory'; - -test('can construct HCL instances', () => { - const options: HlcFactoryDependencies = { - now: () => 5, - node: 0, - }; - const factory = new HlcFactory(options); - - const hlc1 = factory.create(); - const hlc2 = factory.create(); - - expect(hlc1.ts).toBe(5); - expect(hlc1.seq).toBe(0); - expect(hlc1.node).toBe(0); - - expect(hlc2.ts).toBe(5); - expect(hlc2.seq).toBe(0); - expect(hlc2.node).toBe(0); -}); diff --git a/src/web3/hlc/__tests__/util.spec.ts b/src/web3/hlc/__tests__/util.spec.ts deleted file mode 100644 index d403a6e494..0000000000 --- a/src/web3/hlc/__tests__/util.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import {create, toDto, inc, Hlc, cmp, cmpDto, merge, HlcDto} from '..'; - -describe('create()', () => { - test('can create an HLC instance', () => { - const hlc = create(5, 0); - expect(hlc.ts).toBe(5); - expect(hlc.seq).toBe(0); - expect(hlc.node).toBe(0); - }); -}); - -describe('toDto()', () => { - test('can serialize an HLC', () => { - const hlc = new Hlc(1, 2, 3); - const dto = toDto(hlc); - expect(dto).toStrictEqual([1, 2, 3]); - }); -}); - -describe('inc()', () => { - test('increments time, if wall clock incremented', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = inc(hlc1, 2); - expect(hlc2.ts).toBe(2); - expect(hlc2.seq).toBe(0); - expect(hlc2.node).toBe(3); - }); - - test('increments sequence number, if wall clock the same', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = inc(hlc1, 1); - expect(hlc2.ts).toBe(1); - expect(hlc2.seq).toBe(3); - expect(hlc2.node).toBe(3); - }); - - test('increments sequence number, if wall clock decremented (for whatever reason)', () => { - const hlc1 = new Hlc(11, 2, 3); - const hlc2 = inc(hlc1, 10); - expect(hlc2.ts).toBe(11); - expect(hlc2.seq).toBe(3); - expect(hlc2.node).toBe(3); - }); -}); - -describe('cmp()', () => { - test('compares by wall clocks, if they are different', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(4, 5, 6); - const result = cmp(hlc1, hlc2); - expect(result).toBe(1 - 4); - }); - - test('compares by wall clocks, if they are different - 2', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(4, 5, 6); - const result = cmp(hlc2, hlc1); - expect(result).toBe(4 - 1); - }); - - test('compares by sequence numbers, if wall clocks equal and sequence numbers different', () => { - const hlc1 = new Hlc(1, 2, 3); - const hlc2 = new Hlc(1, 4, 5); - expect(cmp(hlc1, hlc2)).toBe(2 - 4); - expect(cmp(hlc2, hlc1)).toBe(4 - 2); - }); - - test('compares by node ID, if wall clocks equal and sequence numbers equal', () => { - const hlc1 = new Hlc(1, 2, 55); - const hlc2 = new Hlc(1, 2, 66); - expect(cmp(hlc1, hlc2)).toBe(55 - 66); - expect(cmp(hlc2, hlc1)).toBe(66 - 55); - }); -}); - -describe('cmpDto()', () => { - test('compares by wall clocks, if they are different', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [4, 5, 6]; - const result = cmpDto(hlc1, hlc2); - expect(result).toBe(1 - 4); - }); - - test('compares by wall clocks, if they are different - 2', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [4, 5, 6]; - const result = cmpDto(hlc2, hlc1); - expect(result).toBe(4 - 1); - }); - - test('compares by sequence numbers, if wall clocks equal and sequence numbers different', () => { - const hlc1: HlcDto = [1, 2, 3]; - const hlc2: HlcDto = [1, 4, 5]; - expect(cmpDto(hlc1, hlc2)).toBe(2 - 4); - expect(cmpDto(hlc2, hlc1)).toBe(4 - 2); - }); - - test('compares by node ID, if wall clocks equal and sequence numbers equal', () => { - const hlc1: HlcDto = [1, 2, 55]; - const hlc2: HlcDto = [1, 2, 66]; - expect(cmpDto(hlc1, hlc2)).toBe(55 - 66); - expect(cmpDto(hlc2, hlc1)).toBe(66 - 55); - }); -}); - -describe('merge()', () => { - test('uses wall clock, if wall clock greater than local and remote timestamp', () => { - const local = new Hlc(1, 2, 3); - const remote = new Hlc(4, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(10); - expect(merged.seq).toBe(0); - expect(merged.node).toBe(3); - }); - - test('uses greatest sequence number, when wall clocks equal', () => { - const local = new Hlc(100, 2, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(100); - expect(merged.seq).toBe(5); - expect(merged.node).toBe(3); - }); - - test('uses greatest sequence number, when wall clocks equal - 2', () => { - const local = new Hlc(100, 22, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(100); - expect(merged.seq).toBe(22); - expect(merged.node).toBe(3); - }); - - test('increments local sequence number, if local wall clock is greatest', () => { - const local = new Hlc(200, 22, 3); - const remote = new Hlc(100, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(200); - expect(merged.seq).toBe(23); - expect(merged.node).toBe(3); - }); - - test('increments remote sequence number, if remote wall clock is greatest', () => { - const local = new Hlc(200, 22, 3); - const remote = new Hlc(300, 5, 6); - const merged = merge(local, remote, 10); - expect(merged.ts).toBe(300); - expect(merged.seq).toBe(6); - expect(merged.node).toBe(3); - }); -}); diff --git a/src/web3/hlc/index.ts b/src/web3/hlc/index.ts deleted file mode 100644 index 37ea6e2f06..0000000000 --- a/src/web3/hlc/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type * from './types'; -export * from './util'; -export * from './Hlc'; -export * from './HlcFactory'; diff --git a/src/web3/hlc/types.ts b/src/web3/hlc/types.ts deleted file mode 100644 index 7b03b53d4e..0000000000 --- a/src/web3/hlc/types.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface HybridLogicalClock { - /** Timestamp, wall clock, number in seconds or milliseconds. */ - readonly ts: number; - /** Monotonically incrementing sequence counter. */ - readonly seq: number; - /** Process ID (aka site ID, session ID). */ - readonly node: number; -} - -/** - * Hybrid Logical Clock (HLC) as a tuple, used when serializing for storage in - * CBOR or JSON formats. - */ -export type HlcDto = [ts: number, seq: number, node: number]; diff --git a/src/web3/hlc/util.ts b/src/web3/hlc/util.ts deleted file mode 100644 index c77ece1f06..0000000000 --- a/src/web3/hlc/util.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {Hlc} from './Hlc'; -import type {HlcDto, HybridLogicalClock} from './types'; - -export const create = (now: number, node: number): HybridLogicalClock => { - return new Hlc(now, 0, node); -}; - -export const toDto = (hlc: HybridLogicalClock): HlcDto => [hlc.ts, hlc.seq, hlc.node]; - -export const fromDto = ([ts, seq, node]: HlcDto): HybridLogicalClock => new Hlc(ts, seq, node); - -export const inc = (local: HybridLogicalClock, now: number): HybridLogicalClock => { - if (now > local.ts) return new Hlc(now, 0, local.node); - return new Hlc(local.ts, local.seq + 1, local.node); -}; - -export const cmp = (a: HybridLogicalClock, b: HybridLogicalClock): number => { - if (a.ts !== b.ts) return a.ts - b.ts; - if (a.seq !== b.seq) return a.seq - b.seq; - return a.node - b.node; -}; - -export const cmpDto = ([ts1, seq1, node1]: HlcDto, [ts2, seq2, node2]: HlcDto): number => { - if (ts1 !== ts2) return ts1 - ts2; - if (seq1 !== seq2) return seq1 - seq2; - return node1 - node2; -}; - -export const merge = (local: HybridLogicalClock, remote: HybridLogicalClock, now: number): HybridLogicalClock => { - if (now > local.ts && now > remote.ts) return new Hlc(now, 0, local.node); - if (local.ts === remote.ts) return new Hlc(local.ts, Math.max(local.seq, remote.seq), local.node); - if (local.ts > remote.ts) return new Hlc(local.ts, local.seq + 1, local.node); - return new Hlc(remote.ts, remote.seq + 1, local.node); -}; diff --git a/src/web3/multiformats/Cid.ts b/src/web3/multiformats/Cid.ts deleted file mode 100644 index 7391a6bc1b..0000000000 --- a/src/web3/multiformats/Cid.ts +++ /dev/null @@ -1,121 +0,0 @@ -import * as vuint from '../util/uvint'; -import * as multibase from './multibase'; -import {Multihash} from './Multihash'; -import {Multicodec, MulticodecIpld} from './constants'; - -export type CidVersion = 0 | 1; - -export class Cid { - public static fromBinary(buf: Uint8Array): Cid { - const isV0 = buf[0] === 0x12 && buf[1] === 0x20; - return isV0 ? Cid.fromBinaryV0(buf) : Cid.fromBinaryV1(buf); - } - - public static fromBinaryV0(buf: Uint8Array): Cid { - if (buf[0] !== 0x12 || buf[1] !== 0x20) throw new Error('EXPECTED_CIDV0'); - const hash = new Multihash(buf); - return new Cid(0, Multicodec.DagPb, hash); - } - - public static fromBinaryV1(buf: Uint8Array): Cid { - const [v1Tag, offset1] = vuint.read(buf, 0); - if (v1Tag !== Multicodec.CidV1) throw new Error('EXPECTED_CIDV1'); - const [contentType, offset2] = vuint.read(buf, offset1); - const hash = new Multihash(buf.slice(offset2)); - return new Cid(1, contentType, hash); - } - - public static fromText(text: string): Cid { - if (text.charCodeAt(0) === 81 && text.charCodeAt(1) === 109) { - const buf = multibase.decode('z' + text); - return Cid.fromBinaryV0(buf); - } - const buf = multibase.decode(text); - if (buf[0] === 0x12) throw new Error('UNSUPPORTED_CIDV0'); - return Cid.fromBinaryV1(buf); - } - - public static async fromData(data: Uint8Array, ipldType: MulticodecIpld = MulticodecIpld.Raw): Promise { - const hash = await Multihash.fromData(data); - return new Cid(1, ipldType, hash); - } - - public static async fromCbor(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.Cbor); - } - - public static async fromDagCbor(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.DagCbor); - } - - public static async fromJson(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.Json); - } - - public static async fromDagJson(cbor: Uint8Array): Promise { - return await Cid.fromData(cbor, MulticodecIpld.DagJson); - } - - constructor( - public readonly v: CidVersion, - public readonly ipldType: number, - public readonly hash: Multihash, - ) {} - - public is(cid: Cid): boolean { - return this.v === cid.v && this.ipldType === cid.ipldType && this.hash.is(cid.hash); - } - - public toV0(): Cid { - if (this.v === 0) return this; - return new Cid(0, Multicodec.DagPb, this.hash); - } - - public toV1(): Cid { - if (this.v === 1) return this; - return new Cid(1, this.ipldType, this.hash); - } - - public toBinary(version: CidVersion = this.v): Uint8Array { - if (version === 0) return this.toBinaryV0(); - return this.toBinaryV1(); - } - - public toBinaryV0(): Uint8Array { - return this.hash.buf; - } - - public toBinaryV1(): Uint8Array { - let size = 2; - const contentType = this.ipldType; - if (contentType >= 0b10000000) size += 1; - if (contentType >= 0b10000000_0000000) size += 1; - if (contentType >= 0b10000000_0000000_0000000) throw new Error('UNSUPPORTED_IPLD_TYPE'); - const hash = this.hash; - const hashBuf = hash.buf; - size += hashBuf.length; - const buf = new Uint8Array(size); - buf[0] = Multicodec.CidV1; - const offset = vuint.write(buf, 1, contentType); - buf.set(hashBuf, offset); - return buf; - } - - public toText(format: multibase.BaseNameOrCode = 'base64url'): string { - if (this.v === 0) return this.toTextV0(); - const buf = multibase.encode(format, this.toBinaryV1()); - const str = String.fromCharCode(...buf); - return str; - } - - public toTextV0(): string { - const blob = this.toBinaryV0(); - const buf = multibase.encode('base58btc', blob); - const str = String.fromCharCode(...buf); - return str.slice(1); - } - - public toString(): string { - return this.toText(); - } -} diff --git a/src/web3/multiformats/Multihash.ts b/src/web3/multiformats/Multihash.ts deleted file mode 100644 index 367af4a8e7..0000000000 --- a/src/web3/multiformats/Multihash.ts +++ /dev/null @@ -1,68 +0,0 @@ -import {cmpUint8Array} from '@jsonjoy.com/util/lib/buffers/cmpUint8Array'; -import {crypto} from '../crypto'; -import * as uvint from '../util/uvint'; -import {Multicodec} from './constants'; - -export class Multihash { - public static async fromData(buf: Uint8Array) { - const hash = await crypto.subtle.digest('SHA-256', buf); - const byteLength = hash.byteLength; - const uint8 = new Uint8Array(2 + byteLength); - uint8[0] = Multicodec.Sha2_256; - uint8[1] = byteLength; - uint8.set(new Uint8Array(hash), 2); - return new Multihash(uint8); - } - - public static validate = (buf: Uint8Array) => { - const length = buf.length; - if (length < 2) throw new Error('INVALID_MULTIHASH'); - const [code, offset1] = uvint.read(buf, 0); - switch (code) { - case Multicodec.Sha2_256: { - break; - } - default: { - throw new Error('UNKNOWN_MULTICODEC'); - } - } - const [lengthHash, offset2] = uvint.read(buf, offset1); - if (offset2 + lengthHash !== length) throw new Error('INVALID_MULTIHASH'); - }; - - constructor(public readonly buf: Uint8Array) { - Multihash.validate(buf); - } - - public type(): number { - const [type] = uvint.read(this.buf, 0); - return type; - } - - public length(): number { - const buf = this.buf; - const [, offset] = uvint.read(buf, 0); - const [length] = uvint.read(buf, offset); - return length; - } - - public value(): Uint8Array { - const buf = this.buf; - const [, offset1] = uvint.read(buf, 0); - const [, offset2] = uvint.read(buf, offset1); - return buf.slice(offset2); - } - - public is(hash: Multihash): boolean { - return cmpUint8Array(this.buf, hash.buf); - } - - public toString16(): string { - let res = ''; - for (const byte of this.buf) { - const hex = byte.toString(16); - res += hex.length === 1 ? '0' + hex : hex; - } - return res; - } -} diff --git a/src/web3/multiformats/__tests__/Cid.spec.ts b/src/web3/multiformats/__tests__/Cid.spec.ts deleted file mode 100644 index 046f20c007..0000000000 --- a/src/web3/multiformats/__tests__/Cid.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import {Cid} from '../Cid'; -import {Multicodec, MulticodecIpld} from '../constants'; - -describe('CID v0', () => { - test('can decode a sample CID', () => { - const txt = 'QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB'; - const cid = Cid.fromText(txt); - expect(cid.v).toBe(0); - expect(cid.ipldType).toBe(Multicodec.DagPb); - expect(cid.hash.type()).toBe(Multicodec.Sha2_256); - expect(cid.hash.length()).toBe(32); - expect(cid.hash.value()).toEqual( - new Uint8Array([ - 14, 112, 113, 197, 157, 243, 185, 69, 77, 29, 24, 161, 82, 112, 170, 54, 213, 79, 137, 96, 106, 87, 109, 198, - 33, 117, 122, 253, 68, 173, 29, 46, - ]), - ); - }); - - test('can encode a sample CID back', () => { - const txt = 'QmPK1s3pNYLi9ERiq3BDxKa4XosgWwFRQUydHUtz4YgpqB'; - const cid = Cid.fromText(txt); - const txt2 = cid.toTextV0(); - expect(txt2).toEqual(txt); - }); -}); - -describe('CID v1', () => { - test('can convert CID v0 to v1', () => { - const txt = 'QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR'; - const cid = Cid.fromText(txt); - const cid2 = cid.toV1(); - expect(cid2.toText('base32')).toEqual('bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'); - }); - - test('can convert CID v1 to v2', () => { - const txt = 'bafybeihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku'; - const cid = Cid.fromText(txt); - const cid2 = cid.toV0(); - expect(cid2.toText()).toEqual('QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n'); - }); - - test('can decode a sample CID', () => { - const txt = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'; - const cid = Cid.fromText(txt); - expect(cid.v).toBe(1); - expect(cid.ipldType).toBe(Multicodec.DagPb); - expect(cid.hash.type()).toBe(Multicodec.Sha2_256); - expect(cid.hash.length()).toBe(32); - expect(cid.hash.value()).toEqual( - new Uint8Array([ - 195, 196, 115, 62, 200, 175, 253, 6, 207, 158, 159, 245, 15, 252, 107, 205, 46, 200, 90, 97, 112, 0, 75, 183, 9, - 102, 156, 49, 222, 148, 57, 26, - ]), - ); - }); - - test('can encode a sample CID back', () => { - const txt = 'bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi'; - const cid = Cid.fromText(txt); - const txt2 = cid.toText('base32'); - expect(txt2).toEqual(txt); - }); - - test('can create a CID from data', async () => { - const text = 'Merkle–Damgård'; - const data = new TextEncoder().encode(text); - const cid = await Cid.fromData(data); - expect(cid.v).toBe(1); - expect(cid.ipldType).toBe(MulticodecIpld.Raw); - expect(cid.toText('base16')).toBe('f01551220' + '41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); - }); -}); diff --git a/src/web3/multiformats/__tests__/Multihash.spec.ts b/src/web3/multiformats/__tests__/Multihash.spec.ts deleted file mode 100644 index 0d08a23e67..0000000000 --- a/src/web3/multiformats/__tests__/Multihash.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import {Multihash} from '../Multihash'; - -const text = 'Merkle–Damgård'; -const data = new TextEncoder().encode(text); - -test('computes correctly SHA2-256 hash for sample data', async () => { - const hash = await Multihash.fromData(data); - expect(hash.toString16()).toBe('122041dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('by default uses SHA2-256 codec', async () => { - const hash = await Multihash.fromData(data); - expect(hash.type()).toBe(0x12); -}); - -test('returns 32 length for SHA2-256 codec', async () => { - const hash = await Multihash.fromData(data); - expect(hash.length()).toBe(32); -}); - -test('returns correct hash value', async () => { - const hash = await Multihash.fromData(data); - const value = hash.value(); - const hex = [...value].map((byte) => byte.toString(16).padStart(2, '0')).join(''); - expect(hex).toBe('41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('can create a Multihash instance from a hash', async () => { - const hash = new Multihash((await Multihash.fromData(data)).buf); - expect(hash.type()).toBe(0x12); - expect(hash.length()).toBe(32); - const value = hash.value(); - const hex = [...value].map((byte) => byte.toString(16).padStart(2, '0')).join(''); - expect(hex).toBe('41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8'); -}); - -test('throws on hash being too short', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf.slice(0, -1); - const hash2 = new Multihash(hash.buf); - expect(() => new Multihash(buf)).toThrow('INVALID_MULTIHASH'); -}); - -test('throws on unknown hash codec', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf; - buf[0] = 0x00; - expect(() => new Multihash(buf)).toThrow('UNKNOWN_MULTICODEC'); -}); - -test('throws on invalid hash length', async () => { - const hash = await Multihash.fromData(data); - const buf = hash.buf; - buf[1] = 31; - expect(() => new Multihash(buf)).toThrow('INVALID_MULTIHASH'); -}); diff --git a/src/web3/multiformats/constants.ts b/src/web3/multiformats/constants.ts deleted file mode 100644 index 45a919c34e..0000000000 --- a/src/web3/multiformats/constants.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @see https://github.com/multiformats/multicodec/blob/master/table.csv - */ -export const enum Multicodec { - CidV1 = 0x01, - CidV2 = 0x02, - CidV3 = 0x03, - Sha2_256 = 0x12, - Cbor = 0x51, - Raw = 0x55, - DagPb = 0x70, - DagCbor = 0x71, - Libp2pKey = 0x72, - GitRaw = 0x78, - TorrentInfo = 0x7b, - TorrentFile = 0x7c, - DagJose = 0x85, - DagCose = 0x86, - DagJson = 0x0129, - Json = 0x0200, -} - -export const enum MulticodecIpld { - Cbor = Multicodec.Cbor, - Raw = Multicodec.Raw, - DagPb = Multicodec.DagPb, - DagCbor = Multicodec.DagCbor, - Libp2pKey = Multicodec.Libp2pKey, - GitRaw = Multicodec.GitRaw, - TorrentInfo = Multicodec.TorrentInfo, - TorrentFile = Multicodec.TorrentFile, - DagJose = Multicodec.DagJose, - DagCose = Multicodec.DagCose, - DagJson = Multicodec.DagJson, - Json = Multicodec.Json, -} diff --git a/src/web3/multiformats/index.ts b/src/web3/multiformats/index.ts deleted file mode 100644 index d62e00661f..0000000000 --- a/src/web3/multiformats/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './constants'; -export * from './Cid'; diff --git a/src/web3/multiformats/multibase.ts b/src/web3/multiformats/multibase.ts deleted file mode 100644 index 6aaa8655eb..0000000000 --- a/src/web3/multiformats/multibase.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @todo The `multibase` is old, and does not have the best performance. - * Especially because it concatenates Uint8Arrays and returns back another - * Uint8Array. But the output of this text encoding is a string, it could - * encode to string directly. Same as what json-joy Base64 does. - */ -import {encode, decode, BaseNameOrCode} from 'multibase'; - -export {encode, decode, BaseNameOrCode}; diff --git a/src/web3/store/cas/CidCas.ts b/src/web3/store/cas/CidCas.ts deleted file mode 100644 index 08ab0b488e..0000000000 --- a/src/web3/store/cas/CidCas.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type {Cid} from '../../multiformats'; -import type {MulticodecIpld} from '../../multiformats/constants'; - -export interface CidCas { - get(cid: Cid): Promise; - has(cid: Cid): Promise; - del(cid: Cid): Promise; - put(value: Uint8Array, ipld?: MulticodecIpld): Promise; -} diff --git a/src/web3/store/cas/CidCasMemory.ts b/src/web3/store/cas/CidCasMemory.ts deleted file mode 100644 index dfec3d53ed..0000000000 --- a/src/web3/store/cas/CidCasMemory.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {Cid} from '../../multiformats'; -import {MulticodecIpld} from '../../multiformats/constants'; -import type {CidCas} from './CidCas'; - -export class CidCasMemory implements CidCas { - protected store: Map = new Map(); - - public async get(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - const value = this.store.get(key); - if (!value) throw new Error(`No value for CID: ${key}`); - return value; - } - - public async has(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - return this.store.has(key); - } - - public async del(cid: Cid): Promise { - const key = cid.toText(); - await new Promise((resolve) => setImmediate(resolve)); - this.store.delete(key); - } - - public async put(value: Uint8Array, ipldType: MulticodecIpld = MulticodecIpld.Raw): Promise { - const cid = await Cid.fromData(value, ipldType); - await new Promise((resolve) => setImmediate(resolve)); - const key = cid.toText(); - this.store.set(key, value); - return cid; - } - - public async size(): Promise { - return this.store.size; - } -} diff --git a/src/web3/store/cas/CidCasStruct.ts b/src/web3/store/cas/CidCasStruct.ts deleted file mode 100644 index a8fdcbb918..0000000000 --- a/src/web3/store/cas/CidCasStruct.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {Cid, MulticodecIpld} from '../../multiformats'; -import type {CidCas} from './CidCas'; -import type {IpldCodec} from '../../codec'; - -export class CidCasStruct { - constructor( - protected readonly cas: CidCas, - protected readonly ipldType: MulticodecIpld, - protected readonly codec: IpldCodec, - ) {} - - public async get(cid: Cid): Promise { - const blob = await this.cas.get(cid); - return this.codec.decoder.decode(blob); - } - - public has(cid: Cid): Promise { - return this.cas.has(cid); - } - - public async del(cid: Cid): Promise { - await this.cas.del(cid); - } - - public async put(value: unknown): Promise { - const blob = this.codec.encoder.encode(value); - return this.cas.put(blob, this.ipldType); - } -} diff --git a/src/web3/store/cas/CidCasStructCbor.ts b/src/web3/store/cas/CidCasStructCbor.ts deleted file mode 100644 index 08f7cd4f8e..0000000000 --- a/src/web3/store/cas/CidCasStructCbor.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {MulticodecIpld} from '../../multiformats'; -import {CidCasStruct} from './CidCasStruct'; -import {cbor} from '../../codec/codecs/cbor'; -import type {CidCas} from './CidCas'; -import type {IpldCodec} from '../../codec'; - -export class CidCasStructCbor extends CidCasStruct { - constructor( - protected readonly cas: CidCas, - protected readonly codec: IpldCodec = cbor, - ) { - super(cas, MulticodecIpld.Cbor, codec); - } -} diff --git a/src/web3/store/cas/README.md b/src/web3/store/cas/README.md deleted file mode 100644 index aea0cb6ac5..0000000000 --- a/src/web3/store/cas/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Content Addressable Storage (CAS) - -Content Addressable Storage (CAS) is a blob key-value database, where the key -is always the hash of the value (content), usually SHA256. diff --git a/src/web3/store/cas/__tests__/CidCasMemory.spec.ts b/src/web3/store/cas/__tests__/CidCasMemory.spec.ts deleted file mode 100644 index 8619f5ac53..0000000000 --- a/src/web3/store/cas/__tests__/CidCasMemory.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {CidCasMemory} from '../CidCasMemory'; - -describe('put()', () => { - test('can store and read a value', async () => { - const cas = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas.put(value); - const stored = await cas.get(cid); - expect(stored).toEqual(value); - }); -}); - -describe('del()', () => { - test('can remove an item', async () => { - const cas = new CidCasMemory(); - const value = new Uint8Array([25]); - const cid = await cas.put(value); - expect(await cas.has(cid)).toBe(true); - expect(await cas.has(cid)).toBe(true); - await cas.del(cid); - expect(await cas.has(cid)).toBe(false); - expect(await cas.has(cid)).toBe(false); - }); - - test('does not throw when deleting non-existing item', async () => { - const cas1 = new CidCasMemory(); - const cas2 = new CidCasMemory(); - const value = new Uint8Array([25]); - const cid = await cas1.put(value); - await cas2.del(cid); - await cas2.del(cid); - await cas2.del(cid); - }); -}); - -describe('has()', () => { - test('can check if item exists', async () => { - const cas1 = new CidCasMemory(); - const cas2 = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - const has2 = await cas2.has(cid); - expect(has1).toBe(true); - expect(has2).toBe(false); - }); - - test('returns false for deleted item', async () => { - const cas1 = new CidCasMemory(); - const value = new Uint8Array([1, 2, 3]); - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - expect(has1).toBe(true); - await cas1.del(cid); - const has2 = await cas1.has(cid); - expect(has2).toBe(false); - }); -}); diff --git a/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts b/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts deleted file mode 100644 index e7f400fd42..0000000000 --- a/src/web3/store/cas/__tests__/CidCasStructCbor.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import {CidCasMemory} from '../CidCasMemory'; -import {CidCasStructCbor} from '../CidCasStructCbor'; - -const setup = () => { - const cas = new CidCasStructCbor(new CidCasMemory()); - return {cas}; -}; - -describe('put()', () => { - test('can store and read a value', async () => { - const {cas} = setup(); - const value = {foo: 'bar', baz: 123}; - const cid = await cas.put(value); - const stored = await cas.get(cid); - expect(stored).toEqual(value); - }); -}); - -describe('del()', () => { - test('can remove an item', async () => { - const {cas} = setup(); - const value = {foo: 'bar'}; - const cid = await cas.put(value); - expect(await cas.has(cid)).toBe(true); - expect(await cas.has(cid)).toBe(true); - await cas.del(cid); - expect(await cas.has(cid)).toBe(false); - expect(await cas.has(cid)).toBe(false); - }); - - test('does not throw when deleting non-existing item', async () => { - const {cas: cas1} = setup(); - const {cas: cas2} = setup(); - const value = {foo: 'bar', baz: 123}; - const cid = await cas1.put(value); - await cas2.del(cid); - await cas2.del(cid); - await cas2.del(cid); - }); -}); - -describe('has()', () => { - test('can check if item exists', async () => { - const {cas: cas1} = setup(); - const {cas: cas2} = setup(); - const value = {foo: 'bar'}; - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - const has2 = await cas2.has(cid); - expect(has1).toBe(true); - expect(has2).toBe(false); - }); - - test('returns false for deleted item', async () => { - const {cas: cas1} = setup(); - const value = {foo: 'bar'}; - const cid = await cas1.put(value); - const has1 = await cas1.has(cid); - expect(has1).toBe(true); - await cas1.del(cid); - const has2 = await cas1.has(cid); - expect(has2).toBe(false); - }); -}); diff --git a/src/web3/util/__tests__/uvint.spec.ts b/src/web3/util/__tests__/uvint.spec.ts deleted file mode 100644 index de99c3cfc1..0000000000 --- a/src/web3/util/__tests__/uvint.spec.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {read, write} from '../uvint'; - -describe('read', () => { - test('can read single byte uvint', () => { - expect(read(new Uint8Array([0]), 0)).toEqual([0, 1]); - expect(read(new Uint8Array([1]), 0)).toEqual([1, 1]); - expect(read(new Uint8Array([65]), 0)).toEqual([65, 1]); - expect(read(new Uint8Array([0b1111111]), 0)).toEqual([0b1111111, 1]); - }); - - test('can read two byte uvint', () => { - expect(read(new Uint8Array([0b10000000, 0b00000001]), 0)).toEqual([0x80, 2]); - expect(read(new Uint8Array([0b11111111, 0b00000001]), 0)).toEqual([0xff, 2]); - expect(read(new Uint8Array([0b10101100, 0b00000010]), 0)).toEqual([0x012c, 2]); - }); - - test('can read three byte uvint', () => { - expect(read(new Uint8Array([0b10000000, 0b10000000, 0b00000001]), 0)).toEqual([0x4000, 3]); - }); -}); - -describe('write', () => { - test('can write single byte uvint', () => { - const buf = new Uint8Array(4); - const offset = write(buf, 1, 5); - expect(offset).toEqual(2); - expect(buf[1]).toEqual(5); - }); - - test('can write two byte uvint', () => { - const buf = new Uint8Array(4); - const offset = write(buf, 2, 222); - expect(offset).toEqual(4); - expect(buf[2]).toEqual(222); - expect(buf[3]).toEqual(1); - }); - - describe('can write different positive integers', () => { - const integers = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0b0_0000000_0011100_1111101, - 0b0_0000100_0011100_1111101, 0b0_0000101_1011100_1111101, 0b0_0010101_1011100_1111101, - 0b0_0110101_1011100_1111101, 0b0_0111101_1011100_1111101, 0b0_1000000_0000000_0100000, - 0b0_1011000_0000000_0100000, 0b0_1011000_0000000_0100001, 0b0_1011000_0000000_0101111, - 0b0_1011000_0000000_0111111, 0b0_1011000_1110000_0111111, 0b0_1011111_1110000_0111111, - 0b0_1111111_1110000_0111111, 0b0_1111111_1110110_0111111, 0b0_1111111_1111111_1111111, - ]; - for (const int of integers) { - test(String(int), () => { - const buf = new Uint8Array(5); - const offset = write(buf, 1, int); - expect(offset).toBeLessThanOrEqual(4); - const [value, newOffset] = read(buf, 1); - expect(value).toEqual(int); - expect(newOffset).toEqual(offset); - }); - } - }); -}); diff --git a/src/web3/util/uvint.ts b/src/web3/util/uvint.ts deleted file mode 100644 index f92b8aea70..0000000000 --- a/src/web3/util/uvint.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Reads an unsigned variable length integer in MSB Multicodec format up to - * 3 bytes. - * - * @see https://github.com/multiformats/unsigned-varint - * - * @param buf Blob from where to read the unsigned variable integer. - * @param offset Offset from where to start reading. - * @returns - */ -export const read = (buf: Uint8Array, offset: number): [value: number, offset: number] => { - const octet1 = buf[offset++]; - if (!(octet1 & 0b10000000)) return [octet1, offset]; - const octet2 = buf[offset++]; - if (!(octet2 & 0b10000000)) return [(octet2 << 7) | (octet1 & 0b01111111), offset]; - const octet3 = buf[offset++]; - if (!(octet3 & 0b10000000)) return [(octet3 << 14) | ((octet2 & 0b01111111) << 7) | (octet1 & 0b01111111), offset]; - throw new Error('UNSUPPORTED_UVINT'); -}; - -/** - * Write up to 3 bytes of an unsigned variable length integer in MSB Multicodec - * format. - * - * @param buf Blob where to write the unsigned variable integer. - * @param offset Position where to start writing. - * @param value Value to write. - */ -export const write = (buf: Uint8Array, offset: number, value: number): number => { - if (value < 0b10000000) { - buf[offset] = value; - return offset + 1; - } - if (value < 0b10000000_00000000) { - buf[offset] = (value & 0b01111111) | 0b10000000; - buf[offset + 1] = value >> 7; - return offset + 2; - } - if (value < 0b10000000_00000000_00000000) { - buf[offset] = (value & 0b01111111) | 0b10000000; - buf[offset + 1] = ((value >> 7) & 0b01111111) | 0b10000000; - buf[offset + 2] = value >> 14; - return offset + 3; - } - throw new Error('UNSUPPORTED_UVINT'); -};