From cb5c2fcf7bfd4df8c419bcd5535c215aab562cf0 Mon Sep 17 00:00:00 2001 From: Paul Le Cam Date: Fri, 23 Feb 2024 15:52:03 +0000 Subject: [PATCH] fix(cli,http-client,stream-model-instance): encode genesis commit in multiquery (#3172) --- packages/cli/package.json | 1 + .../cli/src/__tests__/ceramic-daemon.test.ts | 30 +++++++ packages/cli/src/ceramic-daemon.ts | 13 +-- .../http-client/src/ceramic-http-client.ts | 1 + .../model-instance-document-handler.test.ts | 88 +++++++++---------- .../src/model-instance-document.ts | 8 +- 6 files changed, 87 insertions(+), 54 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index d03c05f41b..4a9b7d3179 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -82,6 +82,7 @@ "devDependencies": { "@ceramicnetwork/common-test-utils": "^3.1.0", "@ceramicnetwork/stream-model": "^4.1.0", + "@ceramicnetwork/stream-model-instance": "^4.1.0", "@ceramicnetwork/stream-tile-handler": "^5.1.0", "@stablelib/sha256": "^1.0.1", "@types/express": "^4.17.21", diff --git a/packages/cli/src/__tests__/ceramic-daemon.test.ts b/packages/cli/src/__tests__/ceramic-daemon.test.ts index 2ac1db55b6..d5758742f0 100644 --- a/packages/cli/src/__tests__/ceramic-daemon.test.ts +++ b/packages/cli/src/__tests__/ceramic-daemon.test.ts @@ -26,6 +26,7 @@ import { CommonTestUtils as TestUtils } from '@ceramicnetwork/common-test-utils' import { EventSource } from 'cross-eventsource' import { AggregationDocument, JsonAsString } from '@ceramicnetwork/codecs' import { Model, ModelDefinition } from '@ceramicnetwork/stream-model' +import { ModelInstanceDocument } from '@ceramicnetwork/stream-model-instance' import { decode } from 'codeco' const seed = 'SEED' @@ -607,6 +608,35 @@ describe('Ceramic interop: core <> http-client', () => { expect(resCore[streamId.toString()].metadata).toEqual(metadata) expect(resClient[streamId.toString()].metadata).toEqual(metadata) }) + + it('handles genesis commit encoding', async () => { + const model = await Model.create(client, { + name: 'TestModel', + version: '2.0', + accountRelation: { type: 'set', fields: ['unique'] }, + interface: false, + implements: [], + schema: { + type: 'object', + properties: { + unique: { type: 'string' }, + }, + required: ['unique'], + additionalProperties: false, + }, + }) + + const meta = { controller: core.did?.id.toString(), deterministic: true, model: model.id } + const genesis = await ModelInstanceDocument.makeGenesis(client, null, meta, ['test']) + const streamId = await StreamID.fromGenesis(ModelInstanceDocument.STREAM_TYPE_ID, genesis) + const id = streamId.toString() + + const resCore = await core.multiQuery([{ genesis, streamId }]) + expect(resCore[id]).toBeInstanceOf(ModelInstanceDocument) + + const resClient = await client.multiQuery([{ genesis, streamId }]) + expect(resClient[id]).toBeInstanceOf(ModelInstanceDocument) + }) }) describeIfV3('pin api', () => { diff --git a/packages/cli/src/ceramic-daemon.ts b/packages/cli/src/ceramic-daemon.ts index a7b4ffaab2..5525a584e3 100644 --- a/packages/cli/src/ceramic-daemon.ts +++ b/packages/cli/src/ceramic-daemon.ts @@ -911,12 +911,13 @@ export class CeramicDaemon { * Load multiple streams and paths using an array of multiqueries */ async multiQuery(req: Request, res: Response): Promise { - const { queries } = req.body - const { timeout } = req.body - - const results = await this.ceramic.multiQuery(queries, timeout) - const response = Object.entries(results).reduce((acc, e) => { - const [k, v] = e + const body = req.body as MultiQueries + const queries = body.queries.map((q) => ({ + ...q, + genesis: q.genesis ? StreamUtils.deserializeCommit(q.genesis) : undefined, + })) + const results = await this.ceramic.multiQuery(queries, body.timeout) + const response = Object.entries(results).reduce((acc, [k, v]) => { acc[k] = StreamUtils.serializeState(v.state) return acc }, {}) diff --git a/packages/http-client/src/ceramic-http-client.ts b/packages/http-client/src/ceramic-http-client.ts index d27060912c..9b9966ee01 100644 --- a/packages/http-client/src/ceramic-http-client.ts +++ b/packages/http-client/src/ceramic-http-client.ts @@ -161,6 +161,7 @@ export class CeramicClient implements StreamReaderWriter { const queriesJSON = queries.map((q) => { return { ...q, + genesis: q.genesis ? StreamUtils.serializeCommit(q.genesis) : undefined, streamId: typeof q.streamId === 'string' ? q.streamId : q.streamId.toString(), } }) diff --git a/packages/stream-model-instance-handler/src/__tests__/model-instance-document-handler.test.ts b/packages/stream-model-instance-handler/src/__tests__/model-instance-document-handler.test.ts index 86d82c2745..dcfad3b9be 100644 --- a/packages/stream-model-instance-handler/src/__tests__/model-instance-document-handler.test.ts +++ b/packages/stream-model-instance-handler/src/__tests__/model-instance-document-handler.test.ts @@ -471,7 +471,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('makes genesis commits correctly', async () => { - const commit = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, METADATA) + const commit = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, METADATA) expect(commit).toBeDefined() const expectedGenesis = { @@ -483,7 +483,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('makes genesis commits correctly with context', async () => { - const commit = await ModelInstanceDocument._makeGenesis(context, CONTENT0, METADATA_WITH_CTX) + const commit = await ModelInstanceDocument.makeGenesis(context, CONTENT0, METADATA_WITH_CTX) expect(commit).toBeDefined() const expectedGenesis = { @@ -500,7 +500,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('Takes controller from authenticated DID if controller not specified', async () => { - const commit = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, { + const commit = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, { model: FAKE_MODEL_ID, }) expect(commit).toBeDefined() @@ -514,25 +514,25 @@ describe('ModelInstanceDocumentHandler', () => { }) it('model is required', async () => { - await expect(ModelInstanceDocument._makeGenesis(context.signer, null, {})).rejects.toThrow( + await expect(ModelInstanceDocument.makeGenesis(context.signer, null, {})).rejects.toThrow( /Must specify a 'model' when creating a ModelInstanceDocument/ ) }) it('creates genesis commits uniquely', async () => { - const commit1 = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, METADATA) - const commit2 = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, METADATA) + const commit1 = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, METADATA) + const commit2 = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, METADATA) expect(commit1).not.toEqual(commit2) expect(StreamUtils.isSignedCommitContainer(commit1)).toBeTruthy() }) it('Can create deterministic genesis commit', async () => { - const commit1 = await ModelInstanceDocument._makeGenesis(context.signer, null, { + const commit1 = await ModelInstanceDocument.makeGenesis(context.signer, null, { ...METADATA, deterministic: true, }) - const commit2 = await ModelInstanceDocument._makeGenesis(context.signer, null, { + const commit2 = await ModelInstanceDocument.makeGenesis(context.signer, null, { ...METADATA, deterministic: true, }) @@ -541,12 +541,12 @@ describe('ModelInstanceDocumentHandler', () => { }) it('Can create deterministic genesis commits with a provided unique value', async () => { - const commit1 = await ModelInstanceDocument._makeGenesis( + const commit1 = await ModelInstanceDocument.makeGenesis( context.signer, null, DETERMINISTIC_METADATA ) - const commit2 = await ModelInstanceDocument._makeGenesis( + const commit2 = await ModelInstanceDocument.makeGenesis( context.signer, null, DETERMINISTIC_METADATA, @@ -554,7 +554,7 @@ describe('ModelInstanceDocumentHandler', () => { ) expect(commit2).not.toEqual(commit1) - const commit3 = await ModelInstanceDocument._makeGenesis( + const commit3 = await ModelInstanceDocument.makeGenesis( context.signer, null, DETERMINISTIC_METADATA, @@ -562,7 +562,7 @@ describe('ModelInstanceDocumentHandler', () => { ) expect(commit3).toEqual(commit2) - const commit4 = await ModelInstanceDocument._makeGenesis( + const commit4 = await ModelInstanceDocument.makeGenesis( context.signer, null, DETERMINISTIC_METADATA, @@ -572,7 +572,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies genesis commit correctly', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -594,7 +594,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies genesis commit correctly with small allowable content length', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context.signer, { myData: 'abcdefghijk' }, METADATA_BLOB @@ -616,7 +616,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('genesis commit with content must be signed', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, DETERMINISTIC_METADATA @@ -634,7 +634,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies deterministic genesis commit correctly', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context.signer, null, DETERMINISTIC_METADATA @@ -653,7 +653,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('deterministic genesis commit cannot have content', async () => { - const rawCommit = await ModelInstanceDocument._makeGenesis( + const rawCommit = await ModelInstanceDocument.makeGenesis( context, CONTENT0, DETERMINISTIC_METADATA @@ -676,7 +676,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies genesis commit correctly with context', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context, CONTENT0, METADATA_WITH_CTX @@ -699,7 +699,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('MIDs for Models with SINGLE accountRelations must be created deterministically', async () => { - const commit = await ModelInstanceDocument._makeGenesis(context.signer, null, { + const commit = await ModelInstanceDocument.makeGenesis(context.signer, null, { ...DETERMINISTIC_METADATA, deterministic: false, }) @@ -720,7 +720,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('MIDs for Models without SINGLE accountRelations must be created uniquely', async () => { - const rawCommit = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, { + const rawCommit = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, { ...METADATA, deterministic: true, }) @@ -746,7 +746,7 @@ describe('ModelInstanceDocumentHandler', () => { 'kjzl6cwe1jw147dvq16zluojmraqvwdmbh61dx9e0c59i344lcrsgqfohexp60s' ) - const commit = await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, { + const commit = await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, { model: nonModelStreamId, }) @@ -767,7 +767,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('makes signed commit correctly', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -803,7 +803,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies signed commit correctly', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -849,7 +849,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('MIDs with SET account relation validate signed commit fields', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, null, { ...DETERMINISTIC_METADATA, model: FAKE_MODEL_SET_ID }, @@ -911,7 +911,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('MIDs with SET account relation validate content schema on update', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, null, { ...DETERMINISTIC_METADATA, model: FAKE_MODEL_SET_ID }, @@ -957,7 +957,7 @@ describe('ModelInstanceDocumentHandler', () => { it('multiple consecutive updates', async () => { const deepCopy = (o) => StreamUtils.deserializeState(StreamUtils.serializeState(o)) - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1031,7 +1031,7 @@ describe('ModelInstanceDocumentHandler', () => { myArray: [1, 2], myMultipleType: 1, } - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, customContent, { @@ -1181,7 +1181,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('throws error when applying genesis commit with invalid schema', async () => { - const commit = (await ModelInstanceDocument._makeGenesis( + const commit = (await ModelInstanceDocument.makeGenesis( context.signer, {}, METADATA @@ -1206,12 +1206,12 @@ describe('ModelInstanceDocumentHandler', () => { test('throws error when applying genesis commit with invalid length', async () => { ModelInstanceDocument.MAX_DOCUMENT_SIZE = 10 await expect( - ModelInstanceDocument._makeGenesis(context.signer, { myData: 'abcdefghijk' }, METADATA) + ModelInstanceDocument.makeGenesis(context.signer, { myData: 'abcdefghijk' }, METADATA) ).rejects.toThrow(/which exceeds maximum size/) }) test('throws error when applying signed commit with invalid schema', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1258,7 +1258,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('throws error if commit signed by wrong DID', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis(context.signer, CONTENT0, { + const genesisCommit = (await ModelInstanceDocument.makeGenesis(context.signer, CONTENT0, { controller: 'did:3:fake', model: FAKE_MODEL_ID, })) as SignedCommitContainer @@ -1281,7 +1281,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('throws error if changes metadata', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1325,7 +1325,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('fails to apply commit with invalid prev link', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1368,7 +1368,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('fails to apply commit with invalid id property', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1411,7 +1411,7 @@ describe('ModelInstanceDocumentHandler', () => { }) it('applies anchor commit correctly', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( context.signer, CONTENT0, METADATA @@ -1474,7 +1474,7 @@ describe('ModelInstanceDocumentHandler', () => { const rotateDate = new Date('2022-03-11T21:28:07.383Z') // make and apply genesis with old key - const genesisCommit = (await ModelInstanceDocument._makeGenesis(signerUsingOldKey, CONTENT0, { + const genesisCommit = (await ModelInstanceDocument.makeGenesis(signerUsingOldKey, CONTENT0, { model: FAKE_MODEL_ID, })) as SignedCommitContainer await ipfs.dag.put(genesisCommit, FAKE_CID_1) @@ -1528,7 +1528,7 @@ describe('ModelInstanceDocumentHandler', () => { const rotateDate = new Date('2022-03-11T21:28:07.383Z') // make genesis with new key - const genesisCommit = (await ModelInstanceDocument._makeGenesis(signerUsingNewKey, CONTENT0, { + const genesisCommit = (await ModelInstanceDocument.makeGenesis(signerUsingNewKey, CONTENT0, { model: FAKE_MODEL_ID, })) as SignedCommitContainer await ipfs.dag.put(genesisCommit, FAKE_CID_1) @@ -1556,7 +1556,7 @@ describe('ModelInstanceDocumentHandler', () => { const rotateDate = new Date('2022-03-11T21:28:07.383Z') // make genesis commit using old key - const genesisCommit = (await ModelInstanceDocument._makeGenesis(signerUsingOldKey, CONTENT0, { + const genesisCommit = (await ModelInstanceDocument.makeGenesis(signerUsingOldKey, CONTENT0, { model: FAKE_MODEL_ID, })) as SignedCommitContainer await ipfs.dag.put(genesisCommit, FAKE_CID_1) @@ -1581,7 +1581,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('throws when trying to create a MID with an interface model', async () => { - const commit = (await ModelInstanceDocument._makeGenesis(defaultSigner, CONTENT0, { + const commit = (await ModelInstanceDocument.makeGenesis(defaultSigner, CONTENT0, { controller: METADATA.controller, model: FAKE_MODEL_INTERFACE_ID, })) as SignedCommitContainer @@ -1602,7 +1602,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('validates relations with required model - throws if invalid', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( defaultSigner, { myData: 3, relationID: FAKE_MID_ID2.toString() }, { controller: METADATA.controller, model: FAKE_MODEL_REQUIRED_RELATION_ID } @@ -1625,7 +1625,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('validates relations with required model - model match', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( defaultSigner, { myData: 3, relationID: FAKE_MID_ID.toString() }, { controller: METADATA.controller, model: FAKE_MODEL_REQUIRED_RELATION_ID } @@ -1646,7 +1646,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('validates relations with optional model - linked MID not provided', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( defaultSigner, { myData: 3 }, { controller: METADATA.controller, model: FAKE_MODEL_OPTIONAL_RELATION_ID } @@ -1667,7 +1667,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('validates relations with optional model - linked MID provided', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( defaultSigner, { myData: 3, relationID: FAKE_MID_ID2.toString() }, { controller: METADATA.controller, model: FAKE_MODEL_OPTIONAL_RELATION_ID } @@ -1688,7 +1688,7 @@ describe('ModelInstanceDocumentHandler', () => { }) test('validates relations with interface model', async () => { - const genesisCommit = (await ModelInstanceDocument._makeGenesis( + const genesisCommit = (await ModelInstanceDocument.makeGenesis( defaultSigner, { myData: 3, relationID: FAKE_MID_ID3.toString() }, { controller: METADATA.controller, model: FAKE_MODEL_INTERFACE_RELATION_ID } diff --git a/packages/stream-model-instance/src/model-instance-document.ts b/packages/stream-model-instance/src/model-instance-document.ts index a1f6f7e962..4adc4f07b9 100644 --- a/packages/stream-model-instance/src/model-instance-document.ts +++ b/packages/stream-model-instance/src/model-instance-document.ts @@ -177,7 +177,7 @@ export class ModelInstanceDocument> extends Stream { const signer: CeramicSigner = opts.asDID ? CeramicSigner.fromDID(opts.asDID) : opts.signer || ceramic.signer - const commit = await ModelInstanceDocument._makeGenesis(signer, content, metadata) + const commit = await ModelInstanceDocument.makeGenesis(signer, content, metadata) return ceramic.createStreamFromGenesis>( ModelInstanceDocument.STREAM_TYPE_ID, @@ -203,7 +203,7 @@ export class ModelInstanceDocument> extends Stream { : opts.signer || ceramic.signer metadata = { ...metadata, deterministic: true } - const commit = await ModelInstanceDocument._makeGenesis(signer, null, metadata) + const commit = await ModelInstanceDocument.makeGenesis(signer, null, metadata) return ceramic.createStreamFromGenesis>( ModelInstanceDocument.STREAM_TYPE_ID, commit, @@ -230,7 +230,7 @@ export class ModelInstanceDocument> extends Stream { : opts.signer || ceramic.signer metadata = { ...metadata, deterministic: true } - const commit = await ModelInstanceDocument._makeGenesis(signer, null, metadata, unique) + const commit = await ModelInstanceDocument.makeGenesis(signer, null, metadata, unique) return ceramic.createStreamFromGenesis>( ModelInstanceDocument.STREAM_TYPE_ID, commit, @@ -415,7 +415,7 @@ export class ModelInstanceDocument> extends Stream { * @param metadata - genesis metadata * @param unique - optional array of strings to set the unique header value */ - private static async _makeGenesis( + static async makeGenesis( context: IntoSigner, content: T | null, metadata: ModelInstanceDocumentMetadataArgs,