Skip to content

Commit

Permalink
feat: better API surface and atomic features (#8)
Browse files Browse the repository at this point in the history
* Intermediate refactoring work

* Make defaultValues uniform

* Fix up stuff

* Add flag to disable node verifier process

* All tests passing

* Update comments

* Yarn lint

* Lint

* Fix

* Yarn version
  • Loading branch information
ntn-x2 authored May 2, 2024
1 parent 1f9506f commit 994437d
Show file tree
Hide file tree
Showing 21 changed files with 672 additions and 602 deletions.
2 changes: 2 additions & 0 deletions .yarn/versions/e245aa3a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declined:
- "@kiltprotocol/dip-sdk"
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"check": "tsc -p tsconfig.json",
"clean": "yarn rimraf -g */{cjs,esm}",
"clean:docs": "rimraf docs/api",
"lint": "eslint --ext .ts . && prettier -c .",
"lint:fix": "prettier -w . && eslint --fix --ext .ts .",
"lint": "eslint --ext .ts .",
"lint:fix": "yarn lint --fix",
"prepublish": "yarn exec cp -f ../../LICENSE .",
"publish": "yarn npm publish --access=public",
"test:e2e:peregrine-provider": "yarn build && vitest run tests/peregrine-dip-consumer-template",
Expand Down
10 changes: 10 additions & 0 deletions src/dipProof/extensions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

export type * from "./types.js"

export * as timeBoundDidSignature from "./timeBoundDidSignature.js"
154 changes: 154 additions & 0 deletions src/dipProof/extensions/timeBoundDidSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

import { toChain as didToChain } from "@kiltprotocol/did"
import { BN, u8aToHex } from "@polkadot/util"

import type {
DidUri,
SignExtrinsicCallback,
VerificationKeyRelationship,
VerificationKeyType,
} from "@kiltprotocol/types"
import type { ApiPromise } from "@polkadot/api"
import type { KeyringPair } from "@polkadot/keyring/types"
import type { Call, Hash } from "@polkadot/types/interfaces"
import type { Option } from "@polkadot/types-codec"
import type { Codec } from "@polkadot/types-codec/types"

const defaultValues = {
accountIdRuntimeType: async () => "AccountId",
blockNumberRuntimeType: async () => "u64",
identityDetailsRuntimeType: async () => "Option<u128>",
validUntilOffset: async () => new BN(50),
}

/**
* The options regarding the provider chain provided when generating a cross-chain mortal DID signature.
*/
export type TimeBoundDidSignatureProviderOpts = {
/** The `DidUri` of the DIP subject that is performing the cross-chain operation. */
didUri: DidUri
/** The `VerificationKeyRelationship` to use from the provided DID Document to sign the cross-chain payload. */
keyRelationship: VerificationKeyRelationship
/** The list of `Signers` to use to sign the cross-chain payload. */
signer: SignExtrinsicCallback
}
/**
* The options regarding the consumer chain when generating a cross-chain mortal DID signature.
*/
export type TimeBoundDidSignatureConsumerOpts = {
/** The runtime definition of an `AccountId`. If not provided, the `AccountId` type is used. */
accountIdRuntimeType?: string
/** The `ApiPromise` instance of the provider chain. */
api: ApiPromise
/** The runtime definition of a `BlockNumber`. If not provided, the `u64` type is used. */
blockNumberRuntimeType?: string
/** The `Call` to DID-authorize. */
call: Call
/** The genesis hash to use for the DID signature. If not provided, it is retrieved at runtime from the consumer chain. */
genesisHash?: Hash
/** The runtime definition of the `IdentityDetails`. If not provided, the `Option<u128>` type is used. */
identityDetailsRuntimeType?: string
/** The address of the submitter account on the consumer chain. */
submitterAddress: KeyringPair["address"]
/** The block number until which the DID signature is to be considered fresh. If not provided, the latest best block number + an offset of 50 blocks is used. */
validUntil?: BN
}
/**
* The options object provided when generating a cross-chain mortal DID signature.
*/
export type TimeBoundDidSignatureOpts = {
consumer: TimeBoundDidSignatureConsumerOpts
provider: TimeBoundDidSignatureProviderOpts
}
/**
* The cross-chain DID signature details to be included in the cross-chain DIP proof.
*/
export type TimeBoundDidSignatureRes = {
signature: Uint8Array
type: VerificationKeyType
validUntil: BN
}
/**
* Generate a DID signature to be used in conjunction with a DIP proof to DID-authorize a cross-chain operation.
*
* @param params The signature generation parameters.
*
* @returns The generated cross-chain DID signature.
*/
export async function generateDidSignature({
provider: { didUri, signer, keyRelationship },
consumer: {
api,
call,
submitterAddress,
// Optional
accountIdRuntimeType,
blockNumberRuntimeType,
genesisHash,
identityDetailsRuntimeType,
validUntil,
},
}: TimeBoundDidSignatureOpts): Promise<TimeBoundDidSignatureRes> {
const blockNumber: BN =
validUntil ??
(await api.query.system.number())
.toBn()
.add(await defaultValues.validUntilOffset())
const genesis = genesisHash ?? (await api.query.system.blockHash(0))
const actualIdentityDetailsRuntimeType =
identityDetailsRuntimeType ??
(await defaultValues.identityDetailsRuntimeType())
const identityDetails = (
await api.query.dipConsumer.identityEntries<Option<Codec>>(
didToChain(didUri),
)
).unwrapOr(api.createType(actualIdentityDetailsRuntimeType, null))

const signaturePayload = api
.createType(
`(Call, ${identityDetailsRuntimeType}, ${
accountIdRuntimeType ?? (await defaultValues.accountIdRuntimeType())
}, ${
blockNumberRuntimeType ?? (await defaultValues.blockNumberRuntimeType())
}, Hash)`,
[call, identityDetails, submitterAddress, blockNumber, genesis],
)
.toU8a()
const { signature, keyType } = await signer({
data: signaturePayload,
did: didUri,
keyRelationship,
})
return {
validUntil: blockNumber,
signature,
type: keyType,
}
}

/**
* Transform a [[TimeBoundDidSignatureRes]] into an object that can be used to extend the basic DIP proof generated by [[generateDipSiblingBaseProof]].
*
* @param params The cross-chain DID signature as generated by [[generateDidSignature]].
*
* @returns The CODEC-ready version of the signature.
*/
export function toChain(
signature: TimeBoundDidSignatureRes,
): Record<string, Codec> {
const encodedSignature = {
signature: {
[signature.type]: u8aToHex(signature.signature),
},
validUntil: signature.validUntil,
} as any as Codec
return {
signature: encodedSignature,
}
}
8 changes: 8 additions & 0 deletions src/dipProof/extensions/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

export type * from "./timeBoundDidSignature.js"
11 changes: 11 additions & 0 deletions src/dipProof/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

export type * from "./types.js"

export * from "./subjectIdentity.js"
export * as extensions from "./extensions/index.js"
78 changes: 78 additions & 0 deletions src/dipProof/subjectIdentity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

import { toChain } from "@kiltprotocol/did"

import type { PalletDidLookupLinkableAccountLinkableAccountId } from "@kiltprotocol/augment-api"
import type { DidUri, DidKey } from "@kiltprotocol/types"
import type { ApiPromise } from "@polkadot/api"
import type { Hash } from "@polkadot/types/interfaces"
import type { Codec } from "@polkadot/types-codec/types"

/**
* The options object provided when generating a DIP identity proof.
*/
export type DipIdentityProofOpts = {
/** The `DID` of the subject. */
didUri: DidUri
/** The list of DID verification methods to include in the DIP proof and to reveal to the consumer chain. */
keyIds: Array<DidKey["id"]>
/** A flag indicating whether the web3name should be included in the DIP proof. */
includeWeb3Name: boolean
/** The list of accounts linked to the DID to include in the DIP proof and to reveal to the consumer chain. */
linkedAccounts: readonly PalletDidLookupLinkableAccountLinkableAccountId[]
/** The `ApiPromise` instance for the provider chain. */
providerApi: ApiPromise
/** The version of the DIP proof to generate. */
version: number
}
/**
* The response object for a generated DIP proof.
*/
export type DipIdentityProofRes = {
/** The generated storage proof. */
proof: {
/** The Merkle proof blinded (not revealed) leaves. */
blinded: Codec
/** The Merkle proof revealed leaves. */
revealed: Codec
}
/** The Merkle root hash which the proof is anchored to. */
root: Hash
}
/**
* Generate a DIP proof that reveals the specified information about the DID subject.
*
* @param params The DIP proof params.
*
* @returns The generated basic DIP proof that reveals the specified parts of the DID Document, optionally revealing its web3name and any linked accounts as specified.
*/
export async function generateDipIdentityProof({
didUri,
keyIds,
includeWeb3Name,
linkedAccounts,
providerApi,
version,
}: DipIdentityProofOpts): Promise<DipIdentityProofRes> {
const proof = await providerApi.call.dipProvider.generateProof({
identifier: toChain(didUri),
version,
proofKeys: keyIds.map((keyId) => keyId.substring(1)),
accounts: linkedAccounts,
shouldIncludeWeb3Name: includeWeb3Name,
})

if (proof.isErr) {
throw new Error(providerApi.findError(proof.asErr.toHex()).docs.join("\n"))
}

// TODO: Better way to cast this?
const okProof = proof.asOk.toJSON() as any

return okProof
}
9 changes: 9 additions & 0 deletions src/dipProof/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Copyright (c) 2024, BOTLabs GmbH.
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/

export type * from "./subjectIdentity.js"
export type * from "./extensions/types.js"
6 changes: 4 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* @module @kiltprotocol/dip-sdk
*/

export * from "./runtime.js"
export type * from "./types.js"

export * as stateProof from "./stateProof/index.js"
export * as dipProof from "./dipProof/index.js"
export * from "./sibling.js"
export * from "./utils.js"
29 changes: 0 additions & 29 deletions src/runtime.ts

This file was deleted.

Loading

0 comments on commit 994437d

Please sign in to comment.