From fbe1492395ad98e620a872208530a3f8f61535a9 Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 27 Jan 2022 16:42:49 +0000 Subject: [PATCH] feat: add support for dag-jose codec (#4028) `dag-jose` is now supported out of the box without any other configuration required. Closes: https://github.com/ceramicnetwork/ceramic/issues/207 Co-authored-by: Joel Thorstensson Co-authored-by: Joel Thorstensson Co-authored-by: stephhuynh18 --- .github/workflows/test.yml | 18 +++-- packages/interface-ipfs-core/package.json | 2 + packages/interface-ipfs-core/src/dag/get.js | 80 +++++++++++++++++++++ packages/interface-ipfs-core/src/dag/put.js | 15 ++++ packages/ipfs-core/package.json | 1 + packages/ipfs-core/src/components/index.js | 3 +- packages/ipfs-http-client/package.json | 1 + packages/ipfs-http-client/src/index.js | 3 +- 8 files changed, 111 insertions(+), 12 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3d2dbc1da6..88da6b483e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -210,11 +210,10 @@ jobs: with: build: | npm run link - - run: | - npm install - npm run build - npm run link - npm run test:interop -- --since ${{ github.event.pull_request.base.sha }} --concurrency 1 -- -- -- -t ${{ matrix.type }} + - run: npm install + - run: npm run build + - run: npm run link + - run: npm run test:interop -- --since ${{ github.event.pull_request.base.sha }} --concurrency 1 -- -- -- -t ${{ matrix.type }} - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: flags: interop-${{ matrix.type }} @@ -241,11 +240,10 @@ jobs: - uses: actions/setup-node@v2 with: node-version: lts/* - - run: | - npm install - npm run build - npm run link - npm run ${{ matrix.suite }} -- --since ${{ github.event.pull_request.base.sha }} --concurrency 1 -- -- -t ${{ matrix.type }} + - run: npm install + - run: npm run build + - run: npm run link + - run: npm run ${{ matrix.suite }} -- --since ${{ github.event.pull_request.base.sha }} --concurrency 1 -- -- -t ${{ matrix.type }} - uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 with: flags: interface-${{ matrix.type }} diff --git a/packages/interface-ipfs-core/package.json b/packages/interface-ipfs-core/package.json index 766168d133..4bc2c59bd2 100644 --- a/packages/interface-ipfs-core/package.json +++ b/packages/interface-ipfs-core/package.json @@ -69,7 +69,9 @@ "aegir": "^36.0.1", "blockstore-core": "^1.0.2", "copyfiles": "^2.4.1", + "dag-jose": "^1.0.0", "delay": "^5.0.0", + "did-jwt": "^5.12.1", "err-code": "^3.0.1", "ipfs-core-types": "^0.9.0", "ipfs-unixfs": "^6.0.3", diff --git a/packages/interface-ipfs-core/src/dag/get.js b/packages/interface-ipfs-core/src/dag/get.js index be3b5211c0..4bf02a56b3 100644 --- a/packages/interface-ipfs-core/src/dag/get.js +++ b/packages/interface-ipfs-core/src/dag/get.js @@ -3,17 +3,20 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import * as dagPB from '@ipld/dag-pb' import * as dagCBOR from '@ipld/dag-cbor' +import * as dagJOSE from 'dag-jose' import { importer } from 'ipfs-unixfs-importer' import { UnixFS } from 'ipfs-unixfs' import all from 'it-all' import { CID } from 'multiformats/cid' import { sha256 } from 'multiformats/hashes/sha2' import { base32 } from 'multiformats/bases/base32' +import { base64url } from 'multiformats/bases/base64' import { expect } from 'aegir/utils/chai.js' import { getDescribe, getIt } from '../utils/mocha.js' import testTimeout from '../utils/test-timeout.js' import { identity } from 'multiformats/hashes/identity' import blockstore from '../utils/blockstore-adapter.js' +import { ES256KSigner, createJWS } from 'did-jwt' /** * @typedef {import('ipfsd-ctl').Factory} Factory @@ -42,6 +45,10 @@ export function testGet (factory, options) { * @type {any} */ let cborNode + /** + * @type {dagJOSE.DagJWE} + */ + let joseNode /** * @type {dagPB.PBNode} */ @@ -50,6 +57,10 @@ export function testGet (factory, options) { * @type {any} */ let nodeCbor + /** + * @type {string} + */ + let nodeJose /** * @type {CID} */ @@ -58,6 +69,10 @@ export function testGet (factory, options) { * @type {CID} */ let cidCbor + /** + * @type {CID} + */ + let cidJose before(async () => { const someData = uint8ArrayFromString('some other data') @@ -68,6 +83,12 @@ export function testGet (factory, options) { cborNode = { data: someData } + joseNode = { + protected: 'eyJhbGciOiJkaXIiLCJlbmMiOiJYQzIwUCJ9', + iv: 'DhVb9URR_o_85MOl-hCellwPTtQ_dj6d', + ciphertext: 'EtUsNJcKzEKdFM9DW5Ua5tVyaQRCKsAD', + tag: '-vG17pRSVB2Vycf2MZRgBA' + } nodePb = { Data: uint8ArrayFromString('I am inside a Protobuf'), @@ -83,6 +104,11 @@ export function testGet (factory, options) { await ipfs.dag.put(nodePb, { storeCodec: 'dag-pb', hashAlg: 'sha2-256' }) await ipfs.dag.put(nodeCbor, { storeCodec: 'dag-cbor', hashAlg: 'sha2-256' }) + + const signer = ES256KSigner('278a5de700e29faae8e40e366ec5012b5ec63d36ec77e8a2417154cc1d25383f') + nodeJose = await createJWS(base64url.encode(cidCbor.bytes).slice(1), signer) + cidJose = CID.createV1(dagJOSE.code, await sha256.digest(dagJOSE.encode(nodeJose))) + await ipfs.dag.put(nodeJose, { storeCodec: dagJOSE.name, hashAlg: 'sha2-256' }) }) it('should respect timeout option when getting a DAG node', () => { @@ -158,6 +184,14 @@ export function testGet (factory, options) { it.skip('should get dag-cbor node value two levels deep', (done) => {}) it.skip('should get dag-cbor value via dag-pb node', (done) => {}) + it('should get only a CID, due to resolving locally only', async function () { + const result = await ipfs.dag.get(cidCbor, { + path: 'pb/Data', + localResolve: true + }) + expect(result.value.equals(cidPb)).to.be.true() + }) + it('should get dag-pb value via dag-cbor node', async function () { const result = await ipfs.dag.get(cidCbor, { path: 'pb/Data' @@ -305,5 +339,51 @@ export function testGet (factory, options) { expect(atPath).to.have.deep.property('value').that.is.an.instanceOf(CID) }) + + it('should get a dag-jose node', async () => { + const cid = await ipfs.dag.put(joseNode, { + storeCodec: 'dag-jose', + hashAlg: 'sha2-256' + }) + + const result = await ipfs.dag.get(cid) + + const node = result.value + expect(joseNode).to.eql(node) + }) + + it('should get a dag-jose node with path', async () => { + const result = await ipfs.dag.get(cidJose, { + path: '/' + }) + + const node = result.value + + const cid = CID.createV1(dagJOSE.code, await sha256.digest(dagJOSE.encode(node))) + expect(cid.equals(cidJose)).to.be.true() + }) + + it('should get a dag-jose node local value', async () => { + const result = await ipfs.dag.get(cidJose, { + path: 'payload' + }) + const converted = dagJOSE.toGeneral(nodeJose) + expect(result.value).to.eql('payload' in converted && converted.payload) + }) + + it('should get dag-cbor value via dag-jose node', async function () { + const result = await ipfs.dag.get(cidJose, { + path: 'link/someData' + }) + expect(result.value).to.eql('I am inside a Cbor object') + }) + + it('should get dag-cbor cid via dag-jose node if local resolve', async function () { + const result = await ipfs.dag.get(cidJose, { + path: 'link', + localResolve: true + }) + expect(result.value).to.eql(cidCbor) + }) }) } diff --git a/packages/interface-ipfs-core/src/dag/put.js b/packages/interface-ipfs-core/src/dag/put.js index cd7b45067c..2f0564a442 100644 --- a/packages/interface-ipfs-core/src/dag/put.js +++ b/packages/interface-ipfs-core/src/dag/put.js @@ -34,6 +34,7 @@ export function testPut (factory, options) { const cborNode = { data: uint8ArrayFromString('some other data') } + const joseNode = 'eyJhbGciOiJFUzI1NksifQ.AXESICjDGMg3fEBSX7_fpbBUYF4E61TXLysmLJgfGEpFG8Pu.z7a2MvPWLsd7leOeHyfeA1OcAFC9yy5rn1HD8xCeHz3nFrwyn_Su5xXUoaIxAre3fXhGjPkVSNiCE36AKiaMng' it('should put dag-pb with default hash func (sha2-256)', () => { return ipfs.dag.put(pbNode, { @@ -63,6 +64,20 @@ export function testPut (factory, options) { }) }) + it('should put dag-jose with default hash func (sha2-256)', () => { + return ipfs.dag.put(joseNode, { + storeCodec: 'dag-jose', + hashAlg: 'sha2-256' + }) + }) + + it('should put dag-jose with non-default hash func (sha2-512)', () => { + return ipfs.dag.put(joseNode, { + storeCodec: 'dag-jose', + hashAlg: 'sha2-512' + }) + }) + it('should return the cid', async () => { const cid = await ipfs.dag.put(cborNode, { storeCodec: 'dag-cbor', diff --git a/packages/ipfs-core/package.json b/packages/ipfs-core/package.json index 3dd926d4d0..734d2833f8 100644 --- a/packages/ipfs-core/package.json +++ b/packages/ipfs-core/package.json @@ -77,6 +77,7 @@ "array-shuffle": "^2.0.0", "blockstore-core": "^1.0.2", "blockstore-datastore-adapter": "^2.0.2", + "dag-jose": "^1.0.0", "datastore-core": "^7.0.0", "datastore-pubsub": "^2.0.0", "debug": "^4.1.1", diff --git a/packages/ipfs-core/src/components/index.js b/packages/ipfs-core/src/components/index.js index 658d2e8f25..f74b57f9d3 100644 --- a/packages/ipfs-core/src/components/index.js +++ b/packages/ipfs-core/src/components/index.js @@ -6,6 +6,7 @@ import { UnixFS } from 'ipfs-unixfs' import * as dagPB from '@ipld/dag-pb' import * as dagCBOR from '@ipld/dag-cbor' import * as dagJSON from '@ipld/dag-json' +import * as dagJOSE from 'dag-jose' import { identity } from 'multiformats/hashes/identity' import { bases, hashes, codecs } from 'multiformats/basics' import { initAssets } from 'ipfs-core-config/init-assets' @@ -289,7 +290,7 @@ export async function create (options = {}) { /** @type {BlockCodec[]} */ const blockCodecs = Object.values(codecs); - [dagPB, dagCBOR, dagJSON, id].concat((options.ipld && options.ipld.codecs) || []).forEach(codec => blockCodecs.push(codec)) + [dagPB, dagCBOR, dagJSON, dagJOSE, id].concat((options.ipld && options.ipld.codecs) || []).forEach(codec => blockCodecs.push(codec)) const multicodecs = new Multicodecs({ codecs: blockCodecs, diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 6b656f333d..66719e2242 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -59,6 +59,7 @@ "@ipld/dag-json": "^8.0.1", "@ipld/dag-pb": "^2.1.3", "any-signal": "^3.0.0", + "dag-jose": "^1.0.0", "debug": "^4.1.1", "err-code": "^3.0.1", "ipfs-core-types": "^0.9.0", diff --git a/packages/ipfs-http-client/src/index.js b/packages/ipfs-http-client/src/index.js index bb7d055f72..389b03119d 100644 --- a/packages/ipfs-http-client/src/index.js +++ b/packages/ipfs-http-client/src/index.js @@ -6,6 +6,7 @@ import { Multihashes } from 'ipfs-core-utils/multihashes' import * as dagPB from '@ipld/dag-pb' import * as dagCBOR from '@ipld/dag-cbor' import * as dagJSON from '@ipld/dag-json' +import * as dagJOSE from 'dag-jose' import { identity } from 'multiformats/hashes/identity' import { bases, hashes, codecs } from 'multiformats/basics' import { createBitswap } from './bitswap/index.js' @@ -80,7 +81,7 @@ export function create (options = {}) { /** @type {BlockCodec[]} */ const blockCodecs = Object.values(codecs); - [dagPB, dagCBOR, dagJSON, id].concat((options.ipld && options.ipld.codecs) || []).forEach(codec => blockCodecs.push(codec)) + [dagPB, dagCBOR, dagJSON, dagJOSE, id].concat((options.ipld && options.ipld.codecs) || []).forEach(codec => blockCodecs.push(codec)) const multicodecs = new Multicodecs({ codecs: blockCodecs,