Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: better API surface and atomic features #8

Merged
merged 10 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading