@symfoni/ethr-did-resolver
Version:
Resolve DID documents for ethereum addresses and public keys
1,282 lines (1,229 loc) • 69.3 kB
text/typescript
import { Contract, ContractFactory } from '@ethersproject/contracts'
import { Resolvable, Resolver } from 'did-resolver'
import { getResolver } from '../resolver'
import { EthrDidController } from '../controller'
import { default as EthereumDIDRegistry } from '../config/EthereumDIDRegistry.json'
import { interpretIdentifier, nullAddress, stringToBytes32 } from '../helpers'
import { createProvider, sleep, startMining, stopMining } from './testUtils'
import { arrayify } from '@ethersproject/bytes'
import { SigningKey } from '@ethersproject/signing-key'
jest.setTimeout(30000)
describe('ethrResolver', () => {
// let registry, accounts, did, identity, controller, delegate1, delegate2, ethr, didResolver
let registryContract: Contract,
accounts: string[],
did: string,
identity: string,
controller: string,
delegate1: string,
delegate2: string,
keyAgreementController: string,
didResolver: Resolvable
const web3Provider = createProvider()
beforeAll(async () => {
const factory = ContractFactory.fromSolidity(EthereumDIDRegistry).connect(web3Provider.getSigner(0))
registryContract = await factory.deploy()
registryContract = await registryContract.deployed()
await registryContract.deployTransaction.wait()
const registry = registryContract.address
accounts = await web3Provider.listAccounts()
identity = accounts[1]
controller = accounts[2]
delegate1 = accounts[3]
delegate2 = accounts[4]
keyAgreementController = accounts[5]
did = `did:ethr:dev:${identity}`
didResolver = new Resolver(getResolver({ name: 'dev', provider: web3Provider, registry }))
})
describe('unregistered', () => {
it('resolves document', async () => {
expect.assertions(1)
await expect(didResolver.resolve(did)).resolves.toEqual({
didDocumentMetadata: {},
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${identity}`,
},
],
authentication: [`${did}#controller`],
assertionMethod: [`${did}#controller`],
},
})
})
it('resolves document with publicKey identifier', async () => {
expect.assertions(1)
const pubKey = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
const pubdid = `did:ethr:dev:0x${pubKey}`
await expect(didResolver.resolve(pubdid)).resolves.toEqual({
didDocumentMetadata: {},
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: pubdid,
verificationMethod: [
{
id: `${pubdid}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: pubdid,
blockchainAccountId: `eip155:1337:${identity}`,
},
{
id: `${pubdid}#controllerKey`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: pubdid,
publicKeyHex: pubKey,
},
],
authentication: [`${pubdid}#controller`, `${pubdid}#controllerKey`],
assertionMethod: [`${pubdid}#controller`, `${pubdid}#controllerKey`],
},
})
})
})
describe('controller changed', () => {
it('resolves document', async () => {
expect.assertions(1)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identity, registryContract).changeOwner(controller, { from: identity })
const result = await didResolver.resolve(did)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
],
authentication: [`${did}#controller`],
assertionMethod: [`${did}#controller`],
},
})
})
it('changing controller invalidates the publicKey as identifier', async () => {
expect.assertions(3)
const pubKey = '0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'
const pubdid = `did:ethr:dev:0x${pubKey}`
const { didDocument } = await didResolver.resolve(pubdid)
expect(didDocument).toEqual({
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: pubdid,
verificationMethod: [
{
id: `${pubdid}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: pubdid,
blockchainAccountId: `eip155:1337:${controller}`,
},
],
authentication: [`${pubdid}#controller`],
assertionMethod: [`${pubdid}#controller`],
})
expect(didDocument?.verificationMethod?.length).toBe(1)
expect(didDocument?.authentication?.length).toBe(1)
})
})
describe('delegates', () => {
describe('add signing delegate', () => {
it('resolves document', async () => {
expect.assertions(1)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identity, registryContract).addDelegate('veriKey', delegate1, 86401, {
from: controller,
})
const result = await didResolver.resolve(did)
await expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate1}`,
},
],
authentication: [`${did}#controller`],
assertionMethod: [`${did}#controller`, `${did}#delegate-1`],
},
})
})
})
describe('add auth delegate', () => {
it('resolves document', async () => {
expect.assertions(1)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identity, registryContract).addDelegate('sigAuth', delegate2, 1, {
from: controller,
})
const result = await didResolver.resolve(did)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate1}`,
},
{
id: `${did}#delegate-2`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
],
authentication: [`${did}#controller`, `${did}#delegate-2`],
assertionMethod: [`${did}#controller`, `${did}#delegate-1`, `${did}#delegate-2`],
},
})
})
})
describe('expire automatically', () => {
it('resolves document', async () => {
expect.assertions(1)
//key validity was set to less than 2 seconds
await sleep(4000)
const result = await didResolver.resolve(did)
expect(result).toEqual({
didDocumentMetadata: expect.anything(),
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate1}`,
},
],
authentication: [`${did}#controller`],
assertionMethod: [`${did}#controller`, `${did}#delegate-1`],
},
})
})
})
describe('revokes delegate', () => {
it('resolves document', async () => {
expect.assertions(1)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identity, registryContract).revokeDelegate('veriKey', delegate1, {
from: controller,
})
await sleep(1000)
const result = await didResolver.resolve(did)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
],
authentication: [`${did}#controller`],
assertionMethod: [`${did}#controller`],
},
})
})
})
describe('re-add auth delegate', () => {
it('resolves document', async () => {
expect.assertions(1)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identity, registryContract).addDelegate('sigAuth', delegate2, 86402, {
from: controller,
})
const result = await didResolver.resolve(did)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`],
},
})
})
})
})
describe('attributes', () => {
describe('add publicKey', () => {
it('resolves with EcdsaSecp256k1VerificationKey2019', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).setAttribute(
'did/pub/Secp256k1/veriKey',
'0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
86401,
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`, `${did}#delegate-5`],
})
})
it('resolves with Ed25519VerificationKey2018', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).setAttribute(
'did/pub/Ed25519/veriKey/base64',
'0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
86402,
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`, `${did}#delegate-5`, `${did}#delegate-6`],
})
})
it('resolves with RSAVerificationKey2018', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).setAttribute(
'did/pub/RSA/veriKey/pem',
'-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
86403,
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [
`${did}#controller`,
`${did}#delegate-4`,
`${did}#delegate-5`,
`${did}#delegate-6`,
`${did}#delegate-7`,
],
})
})
it('resolves with X25519KeyAgreementKey2019', async () => {
expect.assertions(1)
const keyAgrDid = `did:ethr:dev:${keyAgreementController}`
await new EthrDidController(keyAgreementController, registryContract).setAttribute(
'did/pub/X25519/enc/base64',
`0x${Buffer.from('MCowBQYDK2VuAyEAEYVXd3/7B4d0NxpSsA/tdVYdz5deYcR1U+ZkphdmEFI=', 'base64').toString('hex')}`,
86404,
{ from: keyAgreementController }
)
const { didDocument } = await didResolver.resolve(keyAgrDid)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: keyAgrDid,
verificationMethod: [
{
id: `${keyAgrDid}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: keyAgrDid,
blockchainAccountId: `eip155:1337:${keyAgreementController}`,
},
{
id: `${keyAgrDid}#delegate-1`,
type: 'X25519KeyAgreementKey2019',
controller: keyAgrDid,
publicKeyBase64: 'MCowBQYDK2VuAyEAEYVXd3/7B4d0NxpSsA/tdVYdz5deYcR1U+ZkphdmEFI=',
},
],
authentication: [`${keyAgrDid}#controller`],
assertionMethod: [`${keyAgrDid}#controller`, `${keyAgrDid}#delegate-1`],
keyAgreement: [`${keyAgrDid}#delegate-1`],
})
})
})
describe('add service endpoints', () => {
it('resolves document', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).setAttribute(
stringToBytes32('did/svc/HubService'),
'https://hubs.uport.me',
86405,
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [
`${did}#controller`,
`${did}#delegate-4`,
`${did}#delegate-5`,
`${did}#delegate-6`,
`${did}#delegate-7`,
],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
],
})
})
})
describe('add expanded service endpoints', () => {
it('resolves document', async () => {
expect.assertions(2)
await new EthrDidController(identity, registryContract).setAttribute(
stringToBytes32('did/svc/HubService'),
JSON.stringify({ uri: 'https://hubs.uport.me', transportType: 'http' }),
86405,
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [
`${did}#controller`,
`${did}#delegate-4`,
`${did}#delegate-5`,
`${did}#delegate-6`,
`${did}#delegate-7`,
],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
{
id: `${did}#service-2`,
type: 'HubService',
serviceEndpoint: { uri: 'https://hubs.uport.me', transportType: 'http' },
},
],
})
await new EthrDidController(identity, registryContract).setAttribute(
stringToBytes32('did/svc/HubService'),
JSON.stringify([
{ uri: 'https://hubs.uport.me', transportType: 'http' },
{ uri: 'libp2p.star/123', transportType: 'libp2p' },
]),
86405,
{ from: controller }
)
const { didDocument: updatedDidDocument } = await didResolver.resolve(did)
expect(updatedDidDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-5`,
type: 'EcdsaSecp256k1VerificationKey2019',
controller: did,
publicKeyHex: '02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [
`${did}#controller`,
`${did}#delegate-4`,
`${did}#delegate-5`,
`${did}#delegate-6`,
`${did}#delegate-7`,
],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
{
id: `${did}#service-2`,
type: 'HubService',
serviceEndpoint: { uri: 'https://hubs.uport.me', transportType: 'http' },
},
{
id: `${did}#service-3`,
type: 'HubService',
serviceEndpoint: [
{ uri: 'https://hubs.uport.me', transportType: 'http' },
{ uri: 'libp2p.star/123', transportType: 'libp2p' },
],
},
],
})
// undo side effects of this test
await new EthrDidController(identity, registryContract).revokeAttribute(
stringToBytes32('did/svc/HubService'),
JSON.stringify([
{ uri: 'https://hubs.uport.me', transportType: 'http' },
{ uri: 'libp2p.star/123', transportType: 'libp2p' },
]),
{ from: controller }
)
// undo side effects of this test
await new EthrDidController(identity, registryContract).revokeAttribute(
stringToBytes32('did/svc/HubService'),
JSON.stringify({ uri: 'https://hubs.uport.me', transportType: 'http' }),
{ from: controller }
)
})
})
})
describe('revoke publicKey', () => {
it('resolves without EcdsaSecp256k1VerificationKey2019', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).revokeAttribute(
'did/pub/Secp256k1/veriKey',
'0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-6`,
type: 'Ed25519VerificationKey2018',
controller: did,
publicKeyBase64: Buffer.from(
'02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
'hex'
).toString('base64'),
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`, `${did}#delegate-6`, `${did}#delegate-7`],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
],
})
})
it('resolves without Ed25519VerificationKey2018', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).revokeAttribute(
'did/pub/Ed25519/veriKey/base64',
'0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
{
id: `${did}#delegate-7`,
type: 'RSAVerificationKey2018',
controller: did,
publicKeyPem: '-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`, `${did}#delegate-7`],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
],
})
})
it('resolves without RSAVerificationKey2018', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).revokeAttribute(
stringToBytes32('did/pub/RSA/veriKey/pem'),
'-----BEGIN PUBLIC KEY...END PUBLIC KEY-----\r\n',
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`],
service: [
{
id: `${did}#service-1`,
type: 'HubService',
serviceEndpoint: 'https://hubs.uport.me',
},
],
})
})
describe('revoke service endpoints', () => {
it('resolves without HubService', async () => {
expect.assertions(1)
await new EthrDidController(identity, registryContract).revokeAttribute(
stringToBytes32('did/svc/HubService'),
'https://hubs.uport.me',
{ from: controller }
)
const { didDocument } = await didResolver.resolve(did)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: did,
verificationMethod: [
{
id: `${did}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${controller}`,
},
{
id: `${did}#delegate-4`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: did,
blockchainAccountId: `eip155:1337:${delegate2}`,
},
],
authentication: [`${did}#controller`, `${did}#delegate-4`],
assertionMethod: [`${did}#controller`, `${did}#delegate-4`],
})
})
})
})
describe('regression', () => {
it('resolves same document with case sensitive eth address (https://github.com/decentralized-identity/ethr-did-resolver/issues/105)', async () => {
expect.assertions(3)
const lowAddress = accounts[5].toLowerCase()
const checksumAddress = interpretIdentifier(lowAddress).address
const lowDid = `did:ethr:dev:${lowAddress}`
const checksumDid = `did:ethr:dev:${checksumAddress}`
await new EthrDidController(lowAddress, registryContract).setAttribute(
'did/pub/Secp256k1/veriKey/hex',
'0x02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71',
86409,
{ from: lowAddress }
)
const didDocumentLow = (await didResolver.resolve(lowDid)).didDocument
const didDocumentChecksum = (await didResolver.resolve(checksumDid)).didDocument
expect(lowDid).not.toEqual(checksumDid)
expect(didDocumentLow).toBeDefined()
//we don't care about the actual keys, only about their sameness
expect(JSON.stringify(didDocumentLow).toLowerCase()).toEqual(JSON.stringify(didDocumentChecksum).toLowerCase())
})
it('adds sigAuth to authentication section (https://github.com/decentralized-identity/ethr-did-resolver/issues/95)', async () => {
expect.assertions(1)
const address = accounts[4]
const identifier = `did:ethr:dev:${address}`
const authPubKey = `31303866356238393330623164633235386162353765386630646362363932353963363162316166`
await new EthrDidController(identifier, registryContract).setAttribute(
'did/pub/Ed25519/sigAuth/hex',
`0x${authPubKey}`,
86410,
{ from: address }
)
const { didDocument } = await didResolver.resolve(identifier)
expect(didDocument).toEqual({
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
controller: identifier,
type: 'EcdsaSecp256k1RecoveryMethod2020',
blockchainAccountId: `eip155:1337:${address}`,
},
{
id: `${identifier}#delegate-1`,
controller: identifier,
type: `Ed25519VerificationKey2018`,
publicKeyHex: authPubKey,
},
],
authentication: [`${identifier}#controller`, `${identifier}#delegate-1`],
assertionMethod: [`${identifier}#controller`, `${identifier}#delegate-1`],
})
})
describe('Ed25519VerificationKey2018 in base58 (https://github.com/decentralized-identity/ethr-did-resolver/pull/106)', () => {
it('resolves document', async () => {
expect.assertions(1)
const address = accounts[3]
const identifier = `did:ethr:dev:${address}`
const publicKeyHex = `b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71`
const expectedPublicKeyBase58 = 'DV4G2kpBKjE6zxKor7Cj21iL9x9qyXb6emqjszBXcuhz'
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identifier, registryContract).setAttribute(
'did/pub/Ed25519/veriKey/base58',
`0x${publicKeyHex}`,
86411,
{ from: address }
)
const result = await didResolver.resolve(identifier)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
{
id: `${identifier}#delegate-1`,
type: 'Ed25519VerificationKey2018',
controller: identifier,
publicKeyBase58: expectedPublicKeyBase58,
},
],
authentication: [`${identifier}#controller`],
assertionMethod: [`${identifier}#controller`, `${identifier}#delegate-1`],
},
})
})
})
describe('can deactivate a DID (https://github.com/decentralized-identity/ethr-did-resolver/issues/83)', () => {
it('resolves deactivated document', async () => {
expect.assertions(1)
const address = accounts[6]
const identifier = `did:ethr:dev:${address}`
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await new EthrDidController(identifier, registryContract).changeOwner(nullAddress, { from: address })
const result = await didResolver.resolve(identifier)
expect(result).toEqual({
didDocumentMetadata: {
deactivated: true,
updated: expect.anything(),
versionId: `${blockHeightBeforeChange + 1}`,
},
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': 'https://www.w3.org/ns/did/v1',
id: identifier,
verificationMethod: [],
authentication: [],
assertionMethod: [],
},
})
})
})
describe('versioning', () => {
it('can resolve virgin DID with versionId=latest', async () => {
expect.assertions(1)
const virginAddress = '0xce3080168EE293053bA33b235D7116a3263D29f1'
const virginDID = `did:ethr:dev:${virginAddress}`
const result = await didResolver.resolve(`${virginDID}?versionId=latest`)
expect(result).toEqual({
didDocumentMetadata: {},
didResolutionMetadata: {
contentType: 'application/did+ld+json',
},
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: virginDID,
verificationMethod: [
{
id: `${virginDID}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: virginDID,
blockchainAccountId: `eip155:1337:${virginAddress}`,
},
],
authentication: [`${virginDID}#controller`],
assertionMethod: [`${virginDID}#controller`],
},
})
})
it('can resolve did with versionId before deactivation', async () => {
expect.assertions(1)
const deactivatedDid = `did:ethr:dev:${accounts[6]}`
const { didDocumentMetadata } = await didResolver.resolve(deactivatedDid)
const deactivationBlock = parseInt(didDocumentMetadata.versionId ?? '')
const result = await didResolver.resolve(`${deactivatedDid}?versionId=${deactivationBlock - 1}`)
expect(result).toEqual({
didDocumentMetadata: {
nextVersionId: `${deactivationBlock}`,
nextUpdate: didDocumentMetadata.updated,
},
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': ['https://www.w3.org/ns/did/v1', 'https://w3id.org/security/suites/secp256k1recovery-2020/v2'],
id: deactivatedDid,
verificationMethod: [
{
id: `${deactivatedDid}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: deactivatedDid,
blockchainAccountId: `eip155:1337:${accounts[6]}`,
},
],
authentication: [`${deactivatedDid}#controller`],
assertionMethod: [`${deactivatedDid}#controller`],
},
})
})
it('can resolve modified did with versionId=latest', async () => {
expect.assertions(1)
const address = accounts[7]
const identifier = `did:ethr:dev:${address}`
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
// change owner to self
await new EthrDidController(identifier, registryContract).changeOwner(address, { from: address })
const result = await didResolver.resolve(`${identifier}?versionId=latest`)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${blockHeightBeforeChange + 1}`, updated: expect.anything() },
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
],
authentication: [`${identifier}#controller`],
assertionMethod: [`${identifier}#controller`],
},
})
})
it('can resolve did with versionId before an attribute change', async () => {
expect.assertions(1)
const address = accounts[8]
const identifier = `did:ethr:dev:${address}`
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
const ethrDid = new EthrDidController(identifier, registryContract)
await ethrDid.setAttribute('did/pub/Ed25519/veriKey/hex', `0x11111111`, 86411, { from: address })
await ethrDid.setAttribute('did/pub/Ed25519/veriKey/hex', `0x22222222`, 86412, { from: address })
const result = await didResolver.resolve(`${identifier}?versionId=${blockHeightBeforeChange + 1}`)
expect(result).toEqual({
didDocumentMetadata: {
versionId: `${blockHeightBeforeChange + 1}`,
nextVersionId: `${blockHeightBeforeChange + 2}`,
updated: expect.anything(),
nextUpdate: expect.anything(),
},
didResolutionMetadata: { contentType: 'application/did+ld+json' },
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
{
id: `${identifier}#delegate-1`,
type: 'Ed25519VerificationKey2018',
controller: identifier,
publicKeyHex: '11111111',
},
],
authentication: [`${identifier}#controller`],
assertionMethod: [`${identifier}#controller`, `${identifier}#delegate-1`],
},
})
})
it('can resolve did with versionId before a delegate change', async () => {
expect.assertions(1)
const delegateAddress1 = '0xde1E9a7e00000000000000000000000000000001'
const delegateAddress2 = '0xde1e9a7e00000000000000000000000000000002'
const address = accounts[9]
const identifier = `did:ethr:dev:${address}`
const ethrDid = new EthrDidController(identifier, registryContract)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await ethrDid.addDelegate('veriKey', delegateAddress1, 86401, { from: address })
await ethrDid.addDelegate('veriKey', delegateAddress2, 86402, { from: address })
const result = await didResolver.resolve(`${identifier}?versionId=${blockHeightBeforeChange + 1}`)
expect(result).toEqual({
didDocumentMetadata: {
versionId: `${blockHeightBeforeChange + 1}`,
nextVersionId: `${blockHeightBeforeChange + 2}`,
updated: expect.anything(),
nextUpdate: expect.anything(),
},
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
{
id: `${identifier}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${delegateAddress1}`,
},
],
authentication: [`${identifier}#controller`],
assertionMethod: [`${identifier}#controller`, `${identifier}#delegate-1`],
},
})
})
it('can resolve did with versionId before an owner change', async () => {
expect.assertions(1)
const newOwner = '0xde1e9a7e00000000000000000000000000000003'
const address = accounts[10]
const identifier = `did:ethr:dev:${address}`
const ethrDid = new EthrDidController(identifier, registryContract)
const blockHeightBeforeChange = (await web3Provider.getBlock('latest')).number
await ethrDid.changeOwner(address, { from: address })
await ethrDid.changeOwner(newOwner, { from: address })
const result = await didResolver.resolve(`${identifier}?versionId=${blockHeightBeforeChange + 1}`)
expect(result).toEqual({
didDocumentMetadata: {
versionId: `${blockHeightBeforeChange + 1}`,
nextVersionId: `${blockHeightBeforeChange + 2}`,
updated: expect.anything(),
nextUpdate: expect.anything(),
},
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
],
authentication: [`${identifier}#controller`],
assertionMethod: [`${identifier}#controller`],
},
})
})
it('can resolve did with versionId before an attribute expiration', async () => {
expect.assertions(3)
const delegate = '0xde1E9a7e00000000000000000000000000000001'
const address = accounts[11]
const identifier = `did:ethr:dev:${address}`
await new EthrDidController(identifier, registryContract).addDelegate('sigAuth', delegate, 1, {
from: address,
})
let result = await didResolver.resolve(identifier)
// confirm delegate exists
const versionBeforeExpiry = result.didDocumentMetadata.versionId
expect(result?.didDocument?.verificationMethod?.[1]).toEqual({
id: `${identifier}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${delegate}`,
})
// await expiry
await sleep(4000)
// confirm delegate was removed after expiry
result = await didResolver.resolve(identifier)
expect(result?.didDocument?.verificationMethod?.length).toEqual(1)
// resolve DID before expiry
result = await didResolver.resolve(`${identifier}?versionId=${versionBeforeExpiry}`)
expect(result).toEqual({
didDocumentMetadata: { versionId: `${versionBeforeExpiry}`, updated: expect.anything() },
didResolutionMetadata: expect.anything(),
didDocument: {
'@context': expect.anything(),
id: identifier,
verificationMethod: [
{
id: `${identifier}#controller`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,
blockchainAccountId: `eip155:1337:${address}`,
},
{
id: `${identifier}#delegate-1`,
type: 'EcdsaSecp256k1RecoveryMethod2020',
controller: identifier,