diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..d0be2566 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest All", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + }, + { + "type": "node", + "request": "launch", + "name": "Jest Current File", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["${relativeFile}"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest" + } + } + ] + } + \ No newline at end of file diff --git a/README.md b/README.md index 53c9a563..1bc5454a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ --- title: "Ethr DID Resolver" index: 7 -category: "reference" -type: "content" +category: "ethr-did-resolver" +type: "reference" source: "https://github.com/uport-project/ethr-did-resolver/blob/develop/README.md" --- diff --git a/package.json b/package.json index 12e129fe..ad5a2b1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ethr-did-resolver", - "version": "0.1.1", + "version": "0.1.3", "description": "Resolve DID documents around ethereum addresses", "main": "lib/register.js", "repository": { diff --git a/src/__tests__/register-test.js b/src/__tests__/register-test.js index fb6bc2d0..c0f00b82 100644 --- a/src/__tests__/register-test.js +++ b/src/__tests__/register-test.js @@ -5,9 +5,12 @@ import DidRegistryContract from 'ethr-did-registry' import Web3 from 'web3' import ganache from 'ganache-cli' -const { Secp256k1SignatureAuthentication2018, Secp256k1VerificationKey2018 } = delegateTypes +const { + Secp256k1SignatureAuthentication2018, + Secp256k1VerificationKey2018, +} = delegateTypes -function sleep (seconds) { +function sleep(seconds) { return new Promise((resolve, reject) => setTimeout(resolve, seconds * 1000)) } @@ -17,9 +20,46 @@ describe('ethrResolver', () => { const DidReg = Contract(DidRegistryContract) const web3 = new Web3() web3.setProvider(provider) - const getAccounts = () => new Promise((resolve, reject) => web3.eth.getAccounts((error, accounts) => error ? reject(error) : resolve(accounts))) + const getAccounts = () => + new Promise((resolve, reject) => + web3.eth.getAccounts( + (error, accounts) => (error ? reject(error) : resolve(accounts)) + ) + ) DidReg.setProvider(provider) + const stopMining = () => + new Promise((resolve, reject) => + web3.currentProvider.send( + { + jsonrpc: '2.0', + method: 'miner_stop', + id: new Date().getTime(), + }, + (e, val) => { + if (e) reject(e) + return resolve(val) + } + ) + ) + + const startMining = () => { + new Promise(async (resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: '2.0', + method: 'miner_start', + params: [1], + id: new Date().getTime(), + }, + (e, val) => { + if (e) reject(e) + return resolve(val) + } + ) + }) + } + let registry, accounts, did, identity, owner, delegate1, delegate2 beforeAll(async () => { @@ -33,9 +73,9 @@ describe('ethrResolver', () => { registry = await DidReg.new({ from: accounts[0], gasPrice: 100000000000, - gas: 4712388 //1779962 + gas: 4712388, //1779962 }) - register({provider, registry: registry.address}) + register({ provider, registry: registry.address }) }) describe('unregistered', () => { @@ -43,39 +83,47 @@ describe('ethrResolver', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: identity - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: identity, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + ], }) }) }) describe('owner changed', () => { beforeAll(async () => { - await registry.changeOwner(identity, owner, {from: identity}) + await registry.changeOwner(identity, owner, { from: identity }) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + ], }) }) }) @@ -83,64 +131,88 @@ describe('ethrResolver', () => { describe('delegates', () => { describe('add signing delegate', () => { beforeAll(async () => { - await registry.addDelegate(identity, Secp256k1VerificationKey2018, delegate1, 2, {from: owner}) + await registry.addDelegate( + identity, + Secp256k1VerificationKey2018, + delegate1, + 2, + { from: owner } + ) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate1 - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate1, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + ], }) }) }) describe('add auth delegate', () => { beforeAll(async () => { - await registry.addDelegate(identity, Secp256k1SignatureAuthentication2018, delegate2, 10, {from: owner}) + await registry.addDelegate( + identity, + Secp256k1SignatureAuthentication2018, + delegate2, + 10, + { from: owner } + ) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate1 - }, { - id: `${did}#delegate-2`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-2` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate1, + }, + { + id: `${did}#delegate-2`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-2`, + }, + ], }) }) }) @@ -154,31 +226,42 @@ describe('ethrResolver', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-1` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], }) }) }) describe('revokes delegate', () => { beforeAll(async () => { - await registry.revokeDelegate(identity, Secp256k1SignatureAuthentication2018, delegate2, {from: owner}) + await registry.revokeDelegate( + identity, + Secp256k1SignatureAuthentication2018, + delegate2, + { from: owner } + ) await sleep(1) }) @@ -186,16 +269,20 @@ describe('ethrResolver', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + ], }) }) }) @@ -203,166 +290,447 @@ describe('ethrResolver', () => { describe('re-add auth delegate', () => { beforeAll(async () => { await sleep(3) - await registry.addDelegate(identity, Secp256k1SignatureAuthentication2018, delegate2, 86400, {from: owner}) + await registry.addDelegate( + identity, + Secp256k1SignatureAuthentication2018, + delegate2, + 86400, + { from: owner } + ) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-1` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], }) }) }) }) describe('attributes', () => { - describe('publicKey', () => { + describe('add publicKey', () => { describe('Secp256k1VerificationKey2018', () => { beforeAll(async () => { - await registry.setAttribute(identity, stringToBytes32('did/pub/Secp256k1/veriKey'), '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', 10, {from: owner}) + await registry.setAttribute( + identity, + stringToBytes32('did/pub/Secp256k1/veriKey'), + '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + 10, + { from: owner } + ) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }, { - id: `${did}#delegate-2`, - type: 'Secp256k1VerificationKey2018', - owner: did, - publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71' - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-1` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + { + id: `${did}#delegate-2`, + type: 'Secp256k1VerificationKey2018', + owner: did, + publicKeyHex: + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], }) }) }) describe('Ed25519VerificationKey2018', () => { beforeAll(async () => { - await registry.setAttribute(identity, stringToBytes32('did/pub/Ed25519/veriKey/base64'), '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', 10, {from: owner}) + await registry.setAttribute( + identity, + stringToBytes32('did/pub/Ed25519/veriKey/base64'), + '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + 10, + { from: owner } + ) }) - + it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }, { - id: `${did}#delegate-2`, - type: 'Secp256k1VerificationKey2018', - owner: did, - publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71' - }, { - id: `${did}#delegate-3`, - type: 'Ed25519VerificationKey2018', - owner: did, - publicKeyBase64: Buffer.from('02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', 'hex').toString('base64') - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-1` - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + { + id: `${did}#delegate-2`, + type: 'Secp256k1VerificationKey2018', + owner: did, + publicKeyHex: + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + }, + { + id: `${did}#delegate-3`, + type: 'Ed25519VerificationKey2018', + owner: did, + publicKeyBase64: Buffer.from( + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + 'hex' + ).toString('base64'), + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], }) }) }) }) - describe('service endpoints', () => { + describe('add service endpoints', () => { describe('HubService', () => { beforeAll(async () => { - await registry.setAttribute(identity, stringToBytes32('did/svc/HubService'), 'https://hubs.uport.me', 10, {from: owner}) + await registry.setAttribute( + identity, + stringToBytes32('did/svc/HubService'), + 'https://hubs.uport.me', + 10, + { from: owner } + ) }) it('resolves document', () => { return expect(resolve(did)).resolves.toEqual({ '@context': 'https://w3id.org/did/v1', id: did, - publicKey: [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }, { - id: `${did}#delegate-1`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: delegate2 - }, { - id: `${did}#delegate-2`, - type: 'Secp256k1VerificationKey2018', - owner: did, - publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71' - }, { - id: `${did}#delegate-3`, - type: 'Ed25519VerificationKey2018', - owner: did, - publicKeyBase64: Buffer.from('02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', 'hex').toString('base64') - }], - authentication: [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }, { - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-1` - }], - service: [{ - type: 'HubService', - serviceEndpoint: 'https://hubs.uport.me' - }] + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + { + id: `${did}#delegate-2`, + type: 'Secp256k1VerificationKey2018', + owner: did, + publicKeyHex: + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + }, + { + id: `${did}#delegate-3`, + type: 'Ed25519VerificationKey2018', + owner: did, + publicKeyBase64: Buffer.from( + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + 'hex' + ).toString('base64'), + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], + service: [ + { + type: 'HubService', + serviceEndpoint: 'https://hubs.uport.me', + }, + ], }) }) }) + }) + describe('revoke publicKey', () => { + describe('Secp256k1VerificationKey2018', () => { + beforeAll(async () => { + await registry.revokeAttribute( + identity, + stringToBytes32('did/pub/Secp256k1/veriKey'), + '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + { from: owner } + ) + sleep(1) + }) + it('resolves document', () => { + return expect(resolve(did)).resolves.toEqual({ + '@context': 'https://w3id.org/did/v1', + id: did, + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + { + id: `${did}#delegate-3`, + type: 'Ed25519VerificationKey2018', + owner: did, + publicKeyBase64: Buffer.from( + '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + 'hex' + ).toString('base64'), + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], + service: [ + { + type: 'HubService', + serviceEndpoint: 'https://hubs.uport.me', + }, + ], + }) + }) + }) + + describe('Ed25519VerificationKey2018', () => { + beforeAll(async () => { + await registry.revokeAttribute( + identity, + stringToBytes32('did/pub/Ed25519/veriKey/base64'), + '0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71', + { from: owner } + ) + sleep(1) + }) + it('resolves document', () => { + return expect(resolve(did)).resolves.toEqual({ + '@context': 'https://w3id.org/did/v1', + id: did, + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], + service: [ + { + type: 'HubService', + serviceEndpoint: 'https://hubs.uport.me', + }, + ], + }) + }) + }) + }) + + describe('revoke service endpoints', () => { + describe('HubService', () => { + beforeAll(async () => { + await registry.revokeAttribute( + identity, + stringToBytes32('did/svc/HubService'), + 'https://hubs.uport.me', + { from: owner } + ) + sleep(1) + }) + + it('resolves document', () => { + return expect(resolve(did)).resolves.toEqual({ + '@context': 'https://w3id.org/did/v1', + id: did, + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], + }) + }) + }) + }) + }) + + describe('multiple events in one block', () => { + beforeAll(async () => { + await stopMining() + await Promise.all([ + registry.setAttribute( + identity, + stringToBytes32('did/svc/TestService'), + 'https://test.uport.me', + 10, + { from: owner } + ), + registry.setAttribute( + identity, + stringToBytes32('did/svc/TestService'), + 'https://test.uport.me', + 10, + { from: owner } + ), + sleep(1).then(() => startMining()), + ]) + }) + + it('resolves document', async () => { + expect(await resolve(did)).toEqual({ + '@context': 'https://w3id.org/did/v1', + id: did, + publicKey: [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + { + id: `${did}#delegate-1`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: delegate2, + }, + ], + authentication: [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#delegate-1`, + }, + ], + service: [ + { + type: 'TestService', + serviceEndpoint: 'https://test.uport.me', + }, + ], + }) }) }) describe('error handling', () => { it('rejects promise', () => { - return expect(resolve('did:ethr:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX')).rejects.toEqual(new Error('Not a valid ethr DID: did:ethr:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX')) + return expect( + resolve('did:ethr:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX') + ).rejects.toEqual( + new Error( + 'Not a valid ethr DID: did:ethr:2nQtiQG6Cgm1GYTBaaKAgr76uY7iSexUkqX' + ) + ) }) }) }) diff --git a/src/register.js b/src/register.js index 6a049ea2..daa4232c 100644 --- a/src/register.js +++ b/src/register.js @@ -8,39 +8,49 @@ import DidRegistryContract from '../contracts/ethr-did-registry.json' import { Buffer } from 'buffer' export const REGISTRY = '0xdca7ef03e98e0dc2b855be647c39abe984fcf21b' -export function bytes32toString (bytes32) { - return Buffer.from(bytes32.slice(2), 'hex').toString('utf8').replace(/\0+$/, '') +export function bytes32toString(bytes32) { + return Buffer.from(bytes32.slice(2), 'hex') + .toString('utf8') + .replace(/\0+$/, '') } -export function stringToBytes32 (str) { - const buffstr = '0x' + Buffer.from(str).slice(0, 32).toString('hex') +export function stringToBytes32(str) { + const buffstr = + '0x' + + Buffer.from(str) + .slice(0, 32) + .toString('hex') return buffstr + '0'.repeat(66 - buffstr.length) } export const delegateTypes = { Secp256k1SignatureAuthentication2018: stringToBytes32('sigAuth'), - Secp256k1VerificationKey2018: stringToBytes32('veriKey') + Secp256k1VerificationKey2018: stringToBytes32('veriKey'), } export const attrTypes = { sigAuth: 'SignatureAuthentication2018', - veriKey: 'VerificationKey2018' + veriKey: 'VerificationKey2018', } -export function wrapDidDocument (did, owner, history) { +export function wrapDidDocument(did, owner, history) { const now = new BN(Math.floor(new Date().getTime() / 1000)) // const expired = {} - const publicKey = [{ - id: `${did}#owner`, - type: 'Secp256k1VerificationKey2018', - owner: did, - ethereumAddress: owner - }] + const publicKey = [ + { + id: `${did}#owner`, + type: 'Secp256k1VerificationKey2018', + owner: did, + ethereumAddress: owner, + }, + ] - const authentication = [{ - type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#owner` - }] + const authentication = [ + { + type: 'Secp256k1SignatureAuthentication2018', + publicKey: `${did}#owner`, + }, + ] let delegateCount = 0 const auth = {} @@ -48,7 +58,8 @@ export function wrapDidDocument (did, owner, history) { const services = {} for (let event of history) { let validTo = event.validTo - const key = `${event._eventName}-${event.delegateType || event.name}-${event.delegate || event.value}` + const key = `${event._eventName}-${event.delegateType || + event.name}-${event.delegate || event.value}` if (validTo && validTo.gte(now)) { if (event._eventName === 'DIDDelegateChanged') { delegateCount++ @@ -57,20 +68,22 @@ export function wrapDidDocument (did, owner, history) { case 'sigAuth': auth[key] = { type: 'Secp256k1SignatureAuthentication2018', - publicKey: `${did}#delegate-${delegateCount}` + publicKey: `${did}#delegate-${delegateCount}`, } case 'veriKey': pks[key] = { id: `${did}#delegate-${delegateCount}`, type: 'Secp256k1VerificationKey2018', owner: did, - ethereumAddress: event.delegate + ethereumAddress: event.delegate, } break } } else if (event._eventName === 'DIDAttributeChanged') { const name = bytes32toString(event.name) - const match = name.match(/^did\/(pub|auth|svc)\/(\w+)(\/(\w+))?(\/(\w+))?$/) + const match = name.match( + /^did\/(pub|auth|svc)\/(\w+)(\/(\w+))?(\/(\w+))?$/ + ) if (match) { const section = match[1] const algo = match[2] @@ -82,7 +95,7 @@ export function wrapDidDocument (did, owner, history) { const pk = { id: `${did}#delegate-${delegateCount}`, type: `${algo}${type}`, - owner: did + owner: did, } switch (encoding) { case null: @@ -91,10 +104,16 @@ export function wrapDidDocument (did, owner, history) { pk.publicKeyHex = event.value.slice(2) break case 'base64': - pk.publicKeyBase64 = Buffer.from(event.value.slice(2), 'hex').toString('base64') + pk.publicKeyBase64 = Buffer.from( + event.value.slice(2), + 'hex' + ).toString('base64') break case 'base58': - pk.publicKeyBase58 = Buffer.from(event.value.slice(2), 'hex').toString('base58') + pk.publicKeyBase58 = Buffer.from( + event.value.slice(2), + 'hex' + ).toString('base58') break default: pk.value = event.value @@ -102,13 +121,26 @@ export function wrapDidDocument (did, owner, history) { pks[key] = pk break case 'svc': - services[key] = {type: algo, serviceEndpoint: Buffer.from(event.value.slice(2), 'hex').toString()} + services[key] = { + type: algo, + serviceEndpoint: Buffer.from( + event.value.slice(2), + 'hex' + ).toString(), + } break } } } } else { - if (delegateCount > 0 && (event._eventName === 'DIDDelegateChanged' || (event._eventName === 'DIDAttributeChanged' && delegateType.match(/^did\/publicKey\//))) && validTo.lt(now)) delegateCount-- + if ( + delegateCount > 0 && + (event._eventName === 'DIDDelegateChanged' || + (event._eventName === 'DIDAttributeChanged' && + bytes32toString(event.name).match(/^did\/pub\//))) && + validTo.lt(now) + ) + delegateCount-- delete auth[key] delete pks[key] delete services[key] @@ -119,7 +151,7 @@ export function wrapDidDocument (did, owner, history) { '@context': 'https://w3id.org/did/v1', id: did, publicKey: publicKey.concat(Object.values(pks)), - authentication: authentication.concat(Object.values(auth)) + authentication: authentication.concat(Object.values(auth)), } if (Object.values(services).length > 0) { doc.service = Object.values(services) @@ -128,7 +160,7 @@ export function wrapDidDocument (did, owner, history) { return doc } -function configureProvider (conf = {}) { +function configureProvider(conf = {}) { if (conf.provider) { return conf.provider } else if (conf.web3) { @@ -138,7 +170,7 @@ function configureProvider (conf = {}) { } } -export default function register (conf = {}) { +export default function register(conf = {}) { const provider = configureProvider(conf) const eth = new Eth(provider) const registryAddress = conf.registry || REGISTRY @@ -146,28 +178,37 @@ export default function register (conf = {}) { const didReg = DidReg.at(registryAddress) const logDecoder = abi.logDecoder(DidRegistryContract, false) - const lastChanged = async (identity) => { + const lastChanged = async identity => { const result = await didReg.changed(identity) if (result) { return result['0'] } } - async function changeLog (identity) { + async function changeLog(identity) { const history = [] let previousChange = await lastChanged(identity) while (previousChange) { - const logs = await eth.getLogs({address: registryAddress, topics: [null, `0x000000000000000000000000${identity.slice(2)}`], fromBlock: previousChange, toBlock: previousChange}) + const blockNumber = previousChange + const logs = await eth.getLogs({ + address: registryAddress, + topics: [null, `0x000000000000000000000000${identity.slice(2)}`], + fromBlock: previousChange, + toBlock: previousChange, + }) const events = logDecoder(logs) previousChange = undefined for (let event of events) { history.unshift(event) - previousChange = event.previousChange + if (event.previousChange.lt(blockNumber)) { + previousChange = event.previousChange + } } } return history } - async function resolve (did, parsed) { - if (!parsed.id.match(/^0x[0-9a-fA-F]{40}$/)) throw new Error(`Not a valid ethr DID: ${did}`) + async function resolve(did, parsed) { + if (!parsed.id.match(/^0x[0-9a-fA-F]{40}$/)) + throw new Error(`Not a valid ethr DID: ${did}`) const owner = await didReg.identityOwner(parsed.id) const history = await changeLog(parsed.id) return wrapDidDocument(did, owner['0'], history)