Skip to content

Commit

Permalink
feat(did): Added multiple verification method support & uuid did ur…
Browse files Browse the repository at this point in the history
…l support
  • Loading branch information
Eengineer1 committed Jul 25, 2022
1 parent bcfc198 commit d35f007
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 93 deletions.
29 changes: 28 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"devDependencies": {
"@types/jest": "^28.1.4",
"@types/node": "^18.0.3",
"@types/uuid": "^8.3.4",
"jest": "^28.1.2",
"ts-jest": "^28.0.6",
"ts-node": "^10.8.2",
Expand All @@ -45,6 +46,7 @@
"cosmjs-types": "^0.5.0",
"did-jwt": "^6.2.0",
"multiformats": "^9.7.0",
"uint8arrays": "^3.0.0"
"uint8arrays": "^3.0.0",
"uuid": "^8.3.2"
}
}
4 changes: 0 additions & 4 deletions src/modules/did.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { CheqdSigningStargateClient } from "../signer"
import { DidStdFee, IContext, ISignInputs } from "../types"
import { MsgCreateDid, MsgCreateDidPayload } from "@cheqd/ts-proto/cheqd/v1/tx"
import { MsgCreateDidEncodeObject, typeUrlMsgCreateDid } from "../registry"
import { VerificationMethod } from "@cheqd/ts-proto/cheqd/v1/did"

export class DIDModule extends AbstractCheqdSDKModule {
constructor(signer: CheqdSigningStargateClient){
Expand All @@ -24,9 +23,6 @@ export class DIDModule extends AbstractCheqdSDKModule {
const payload = MsgCreateDidPayload.fromPartial(didPayload)
const signatures = await this._signer.signDidTx(signInputs, payload)

console.warn(payload)
console.warn(signatures)

const value: MsgCreateDid = {
payload,
signatures
Expand Down
51 changes: 26 additions & 25 deletions src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,31 +86,6 @@ export class CheqdSigningStargateClient extends SigningStargateClient {
} */
}

async checkDidSigners(verificationMethods: VerificationMethod[] = []): Promise<TSignerAlgo> {
if (verificationMethods.length === 0) {
throw new Error('No verification methods provided')
}

verificationMethods.forEach((verificationMethod) => {
if (!(Object.values(VerificationMethods) as string[]).includes(verificationMethod.type)) {
throw new Error(`Unsupported verification method type: ${verificationMethod.type}`)
}
if (!this.didSigners[verificationMethod.type]) {
this.didSigners[verificationMethod.type] = EdDSASigner
}
})

return this.didSigners
}

async getDidSigner(verificationMethodId: string, verificationMethods: Partial<VerificationMethod>[]): Promise<(secretKey: Uint8Array) => Signer> {
const verificationMethod = verificationMethods.find(method => method.id === verificationMethodId)?.type
if (!verificationMethod) {
throw new Error(`Verification method for ${verificationMethodId} not found`)
}
return this.didSigners[verificationMethod] ?? EdDSASigner
}

async signAndBroadcast(
signerAddress: string,
messages: readonly EncodeObject[],
Expand Down Expand Up @@ -196,6 +171,32 @@ export class CheqdSigningStargateClient extends SigningStargateClient {
})
}

async checkDidSigners(verificationMethods: Partial<VerificationMethod>[] = []): Promise<TSignerAlgo> {
if (verificationMethods.length === 0) {
throw new Error('No verification methods provided')
}

verificationMethods.forEach((verificationMethod) => {
if (!(Object.values(VerificationMethods) as string[]).includes(verificationMethod.type ?? '')) {
throw new Error(`Unsupported verification method type: ${verificationMethod.type}`)
}
if (!this.didSigners[verificationMethod.type ?? '']) {
this.didSigners[verificationMethod.type ?? ''] = EdDSASigner
}
})

return this.didSigners
}

async getDidSigner(verificationMethodId: string, verificationMethods: Partial<VerificationMethod>[]): Promise<(secretKey: Uint8Array) => Signer> {
await this.checkDidSigners(verificationMethods)
const verificationMethod = verificationMethods.find(method => method.id === verificationMethodId)?.type
if (!verificationMethod) {
throw new Error(`Verification method for ${verificationMethodId} not found`)
}
return this.didSigners[verificationMethod]!
}

async signDidTx(signInputs: ISignInputs[], payload: MsgCreateDidPayload): Promise<SignInfo[]> {
await this.checkDidSigners(payload?.verificationMethod)

Expand Down
20 changes: 19 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ export interface IContext {
}

export enum VerificationMethods {
Multibase58 = 'Ed25519VerificationKey2020',
Base58 = 'Ed25519VerificationKey2020',
JWK = 'JsonWebKey2020',
}

export enum MethodSpecificIdAlgo {
Base58 = 'base58btc',
Uuid = 'uuid',
}

export type TSignerAlgo = {
[key in VerificationMethods as string]?: (secretKey: Uint8Array) => Signer
}
Expand All @@ -41,6 +46,19 @@ export interface IKeyValuePair {
value: any
}

export type TVerificationKeyPrefix = string

export type TVerificationKey<K extends TVerificationKeyPrefix, N extends number> = `${K}-${N}`

export interface IVerificationKeys {
readonly methodSpecificId: TMethodSpecificId
readonly didUrl: `did:cheqd:${CheqdNetwork}:${IVerificationKeys['methodSpecificId']}` extends string ? string : never
readonly keyId: `${IVerificationKeys['didUrl']}#${TVerificationKey<TVerificationKeyPrefix, number>}`
readonly publicKey: string
}

export type TMethodSpecificId = string

export interface DidStdFee {
readonly amount: readonly Coin[]
readonly gas: string
Expand Down
21 changes: 15 additions & 6 deletions tests/modules/did.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { DeliverTxResponse } from "@cosmjs/stargate"
import { fromString, toString } from 'uint8arrays'
import { DIDModule } from "../../src"
import { CheqdSigningStargateClient } from "../../src/signer"
import { CheqdNetwork, DidStdFee, ISignInputs, VerificationMethods } from "../../src/types"
import { createDidPayload, createKeyPairBase64, exampleCheqdNetwork, faucet } from "../testutils.test"
import { CheqdNetwork, DidStdFee, ISignInputs, MethodSpecificIdAlgo, VerificationMethods } from "../../src/types"
import { createDidPayload, createDidVerificationMethod, createKeyPairBase64, createVerificationKeys, exampleCheqdNetwork, faucet } from "../testutils.test"


describe('DIDModule', () => {
Expand All @@ -18,14 +18,15 @@ describe('DIDModule', () => {
})

describe('createDidTx', () => {
jest.setTimeout(20000)
it('should create a new DID', async () => {
jest.setTimeout(10000)

const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic, {prefix: faucet.prefix})
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const didModule = new DIDModule(signer)
const keyPair = createKeyPairBase64()
const didPayload = createDidPayload(keyPair, VerificationMethods.JWK, CheqdNetwork.Testnet)
const verificationKeys = createVerificationKeys(keyPair, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58], [verificationKeys])
const didPayload = createDidPayload(verificationMethods, [verificationKeys])
const signInputs: ISignInputs[] = [
{
verificationMethodId: didPayload.verificationMethod[0].id,
Expand All @@ -49,9 +50,17 @@ describe('DIDModule', () => {
fee
)

console.warn(didTx)
console.warn(`Using payload: ${JSON.stringify(didPayload)}`)
console.warn(`DID Tx: ${JSON.stringify(didTx)}`)

expect(didTx.code).toBe(0)
})
})

describe('updateDidTx', () => {
jest.setTimeout(20000)
it('should update a DID', async () => {

})
})
})
59 changes: 44 additions & 15 deletions tests/signer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,32 @@ import { DirectSecp256k1HdWallet, Registry } from "@cosmjs/proto-signing"
import { base64ToBytes, EdDSASigner } from "did-jwt"
import { typeUrlMsgCreateDid } from "../src/registry"
import { CheqdSigningStargateClient } from "../src/signer"
import { ISignInputs, VerificationMethods } from "../src/types"
import { ISignInputs, MethodSpecificIdAlgo, VerificationMethods } from "../src/types"
import { fromString, toString } from 'uint8arrays'
import { createDidPayload, createKeyPairBase64, exampleCheqdNetwork, faucet } from "./testutils.test"
import { createDidPayload, createDidVerificationMethod, createKeyPairBase64, createVerificationKeys, exampleCheqdNetwork, faucet } from "./testutils.test"
import { verify } from "@stablelib/ed25519"

const nonExistingDid = "did:cHeQd:fantasticnet:123"
const nonExistingKeyId = 'did:cHeQd:fantasticnet:123#key-678'
const nonExistingPublicKeyMultibase = '1234567890'
const nonExistingVerificationMethod = 'ExtraTerrestrialVerificationKey2045'
const nonExistingVerificationDidDocument = {
"authentication": [
"did:cheqd:testnet:z6Jn6NmYkaCepQe2#key-1"
],
"controller": [
"did:cheqd:testnet:z6Jn6NmYkaCepQe2"
],
"id": "did:cheqd:testnet:z6Jn6NmYkaCepQe2",
"verificationMethod": [
{
"controller": "did:cheqd:testnet:z6Jn6NmYkaCepQe2",
"id": "did:cheqd:testnet:z6Jn6NmYkaCepQe2#key-1",
"publicKeyMultibase": "z6Jn6NmYkaCepQe29vgCZQhFfRkN3YpEPiu14F8HbbmqW",
"type": nonExistingVerificationMethod
}
]
}

describe('CheqdSigningStargateClient', () => {
describe('constructor', () => {
Expand All @@ -35,25 +52,29 @@ describe('CheqdSigningStargateClient', () => {
it('can get a signer for a did', async () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const didPayload = createDidPayload(createKeyPairBase64(), VerificationMethods.Multibase58)
const keyPair = createKeyPairBase64()
const verificationKeys = createVerificationKeys(keyPair, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58], [verificationKeys])
const didPayload = createDidPayload(verificationMethods, [verificationKeys])
const didSigner = await signer.getDidSigner(didPayload.verificationMethod[0].id, didPayload.verificationMethod)

expect(didSigner).toBe(EdDSASigner)
})

it('should not return a did payload for a non-supported verification method', async () => {
it('should throw for a non-supported verification method', async () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
//@ts-ignore
const didPayload = createDidPayload(createKeyPairBase64(), 'ExtraTerrestrialVerificationKey2045')

expect(didPayload).toBe(undefined)
await expect(signer.getDidSigner(nonExistingVerificationDidDocument.verificationMethod[0].id, nonExistingVerificationDidDocument.verificationMethod)).rejects.toThrow()
})

it('should throw for non-matching verification method id', async () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const didPayload = createDidPayload(createKeyPairBase64(), VerificationMethods.JWK)
const keyPair = createKeyPairBase64()
const verificationKeys = createVerificationKeys(keyPair, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58], [verificationKeys])
const didPayload = createDidPayload(verificationMethods, [verificationKeys])
await expect(signer.getDidSigner(nonExistingKeyId, didPayload.verificationMethod)).rejects.toThrow()
})
})
Expand All @@ -62,23 +83,29 @@ describe('CheqdSigningStargateClient', () => {
it('it should instantiate a signer for a did', async () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const didPayload = createDidPayload(createKeyPairBase64(), VerificationMethods.Multibase58)
const keyPair = createKeyPairBase64()
const verificationKeys = createVerificationKeys(keyPair, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58], [verificationKeys])
const didPayload = createDidPayload(verificationMethods, [verificationKeys])

const didSigners = await signer.checkDidSigners(didPayload.verificationMethod)

expect(didSigners[VerificationMethods.Multibase58]).toBe(EdDSASigner)
expect(didSigners[VerificationMethods.Base58]).toBe(EdDSASigner)
})

it('should instantiate multiple signers for a did with multiple verification methods', async () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const didPayload = createDidPayload(createKeyPairBase64(), VerificationMethods.Multibase58)

didPayload.verificationMethod.push(createDidPayload(createKeyPairBase64(), VerificationMethods.JWK).verificationMethod[0])
const keyPair1 = createKeyPairBase64()
const keyPair2 = createKeyPairBase64()
const verificationKeys1 = createVerificationKeys(keyPair1, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationKeys2 = createVerificationKeys(keyPair2, MethodSpecificIdAlgo.Base58, 'key-2', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58, VerificationMethods.JWK], [verificationKeys1, verificationKeys2])
const didPayload = createDidPayload(verificationMethods, [verificationKeys1, verificationKeys2])

const didSigners = await signer.checkDidSigners(didPayload.verificationMethod)

expect(didSigners[VerificationMethods.Multibase58]).toBe(EdDSASigner)
expect(didSigners[VerificationMethods.Base58]).toBe(EdDSASigner)
expect(didSigners[VerificationMethods.JWK]).toBe(EdDSASigner)
})

Expand All @@ -101,7 +128,9 @@ describe('CheqdSigningStargateClient', () => {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(faucet.mnemonic)
const signer = await CheqdSigningStargateClient.connectWithSigner(exampleCheqdNetwork.rpcUrl, wallet)
const keyPair = createKeyPairBase64()
const didPayload = createDidPayload(keyPair, VerificationMethods.Multibase58)
const verificationKeys = createVerificationKeys(keyPair, MethodSpecificIdAlgo.Base58, 'key-1', 16)
const verificationMethods = createDidVerificationMethod([VerificationMethods.Base58], [verificationKeys])
const didPayload = createDidPayload(verificationMethods, [verificationKeys])
const signInputs: ISignInputs[] = [
{
verificationMethodId: didPayload.verificationMethod[0].id,
Expand Down
Loading

0 comments on commit d35f007

Please sign in to comment.