diff --git a/src/__tests__/resolve.attribute.test.ts b/src/__tests__/resolve.attribute.test.ts index 88c46d78..c7baed78 100644 --- a/src/__tests__/resolve.attribute.test.ts +++ b/src/__tests__/resolve.attribute.test.ts @@ -18,6 +18,70 @@ describe('attributes', () => { provider = reg.provider }) + describe('invoking createSetAttributeHash', () => { + it('sets the "encodedValue" to the passed hex encoded string (e.g. a public key)', async () => { + expect.assertions(3) + const { address: identity, signer } = await randomAccount(provider) + const { pubKey: attrValue } = await randomAccount(provider) + const attrName = 'did/pub/Secp256k1/veriKey' + const controller = new EthrDidController(identity, registryContract, signer) + const encodeAttributeValueSpy = jest.spyOn(controller, 'encodeAttributeValue') + const ttl = 11111 + await controller.createSetAttributeHash(attrName, attrValue, ttl) + expect(encodeAttributeValueSpy).toHaveBeenCalledWith(attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledTimes(1) + expect(encodeAttributeValueSpy).toHaveReturnedWith(attrValue) + }) + + it('sets the "encodedValue" to a bytes encoded version of the passed attribute value (e.g. a service endpoint)', async () => { + expect.assertions(3) + const { address: identity, signer } = await randomAccount(provider) + const attrValue = 'https://hubs.uport.me/service-endpoints-are-not-hex' + const attrName = 'did/pub/Secp256k1/veriKey' + const controller = new EthrDidController(identity, registryContract, signer) + const encodeAttributeValueSpy = jest.spyOn(controller, 'encodeAttributeValue') + const ttl = 11111 + await controller.createSetAttributeHash(attrName, attrValue, ttl) + expect(encodeAttributeValueSpy).toHaveBeenCalledWith(attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledTimes(1) + const expectedEncodedValue = toUtf8Bytes(attrValue) + expect(encodeAttributeValueSpy).toHaveReturnedWith(expectedEncodedValue) + }) + }) + + describe('invoking createRevokeAttributeHash', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('sets the "encodedValue" to the passed hex encoded string (e.g. a public key)', async () => { + expect.assertions(3) + const { address: identity, signer } = await randomAccount(provider) + const { pubKey: attrValue } = await randomAccount(provider) + const attrName = 'did/pub/Secp256k1/veriKey' + const controller = new EthrDidController(identity, registryContract, signer) + const encodeAttributeValueSpy = jest.spyOn(controller, 'encodeAttributeValue') + await controller.createRevokeAttributeHash(attrName, attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledWith(attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledTimes(1) + expect(encodeAttributeValueSpy).toHaveReturnedWith(attrValue) + }) + + it('sets the "encodedValue" to a bytes encoded version of the passed attribute value (e.g. a service endpoint)', async () => { + expect.assertions(3) + const { address: identity, signer } = await randomAccount(provider) + const attrValue = 'https://hubs.uport.me/service-endpoints-are-not-hex' + const attrName = 'did/pub/Secp256k1/veriKey' + const controller = new EthrDidController(identity, registryContract, signer) + const encodeAttributeValueSpy = jest.spyOn(controller, 'encodeAttributeValue') + await controller.createRevokeAttributeHash(attrName, attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledWith(attrValue) + expect(encodeAttributeValueSpy).toHaveBeenCalledTimes(1) + const expectedEncodedValue = toUtf8Bytes(attrValue) + expect(encodeAttributeValueSpy).toHaveReturnedWith(expectedEncodedValue) + }) + }) + describe('add public keys', () => { it('add EcdsaSecp256k1VerificationKey2019 signing key', async () => { expect.assertions(1) @@ -54,9 +118,9 @@ describe('attributes', () => { it('add Bls12381G2Key2020 assertion key', async () => { expect.assertions(1) const { address: identity, shortDID: did, signer } = await randomAccount(provider) - const pubKey = hexlify(toUtf8Bytes("public key material here")) // encodes to 0x7075626c6963206b6579206d6174657269616c2068657265 in base16 + const pubKey = hexlify(toUtf8Bytes('public key material here')) // encodes to 0x7075626c6963206b6579206d6174657269616c2068657265 in base16 await new EthrDidController(identity, registryContract, signer).setAttribute( - 'did/pub/Bls12381G2Key2020', // attrName must fit into 32 bytes. Anything extra will be truncated. + 'did/pub/Bls12381G2Key2020', // attrName must fit into 32 bytes. Anything extra will be truncated. pubKey, // There's no limit on the size of the public key material 86401 ) @@ -75,7 +139,7 @@ describe('attributes', () => { id: `${did}#delegate-1`, type: 'Bls12381G2Key2020', controller: did, - publicKeyHex: "7075626c6963206b6579206d6174657269616c2068657265", + publicKeyHex: '7075626c6963206b6579206d6174657269616c2068657265', }, ], authentication: [`${did}#controller`], diff --git a/src/controller.ts b/src/controller.ts index 4798e514..b403f7c3 100644 --- a/src/controller.ts +++ b/src/controller.ts @@ -87,6 +87,16 @@ export class EthrDidController { this.did = publicKey ? `did:ethr:${networkString}${publicKey}` : `did:ethr:${networkString}${address}` } + /** + * @returns the encoded attribute value in hex or utf8 bytes + * @param attrValue - the attribute value to encode (e.g. service endpoint, public key, etc.) + * + * @remarks The incoming attribute value may be a hex encoded key, or an utf8 encoded string (like service endpoints) + **/ + encodeAttributeValue(attrValue: string | `0x${string}`): Uint8Array | `0x${string}` { + return isHexString(attrValue) ? attrValue : toUtf8Bytes(attrValue) + } + async getOwner(address: address, blockTag?: BlockTag): Promise { return this.contract.identityOwner(address, { blockTag }) } @@ -295,10 +305,7 @@ export class EthrDidController { async createSetAttributeHash(attrName: string, attrValue: string, exp: number) { const paddedNonce = await this.getPaddedNonceCompatibility(true) - - // The incoming attribute value may be a hex encoded key, or an utf8 encoded string (like service endpoints) - const encodedValue = isHexString(attrValue) ? attrValue : toUtf8Bytes(attrValue) - + const encodedValue = this.encodeAttributeValue(attrValue) const dataToHash = concat([ MESSAGE_PREFIX, await this.contract.getAddress(), @@ -359,13 +366,13 @@ export class EthrDidController { async createRevokeAttributeHash(attrName: string, attrValue: string) { const paddedNonce = await this.getPaddedNonceCompatibility(true) - + const encodedValue = this.encodeAttributeValue(attrValue) const dataToHash = concat([ MESSAGE_PREFIX, await this.contract.getAddress(), paddedNonce, this.address, - getBytes(concat([toUtf8Bytes('revokeAttribute'), encodeBytes32String(attrName), toUtf8Bytes(attrValue)])), + getBytes(concat([toUtf8Bytes('revokeAttribute'), encodeBytes32String(attrName), encodedValue])), ]) return keccak256(dataToHash) }