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: DIP support #13

Merged
merged 26 commits into from
Dec 21, 2023
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: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ jobs:

- name: Test eslint and prettier rules
run: |
yarn install --frozen-lockfile
yarn install --immutable
yarn lint && yarn style
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.env
yarn-error.log

.yarn/*
!.yarn/releases
node_modules
out
874 changes: 874 additions & 0 deletions .yarn/releases/yarn-3.6.0.cjs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-3.6.0.cjs
63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,69 @@ If a call requires a DID assertion method key, either `ATT_MNEMONIC` or `DID_MNE

To run this script, execute `yarn call-authorize` and then copy the HEX-encoded operation to be submitted via [PolkadotJS Apps][polkadot-apps] in `Developer > Extrinsics > Decode`, using the account specified in `SUBMITTER_ADDRESS`.

## Generate a DIP signature for a sibling parachain with a DID key

This script signs any valid HEX-encoded call of any other parachain with the right key re-generated from the provided seedling information, i.e., either with the provided mnemonic, or with the provided combination of base mnemonic and derivation path.

Valid HEX-encoded calls can be generated by interacting with [PolkadotJS Apps][polkadot-apps] under the `Developer > Extrinsics` menu.
Once the right call (i.e., the right pallet and right method) with the right parameters has been specified, the HEX-encoded value under `encoded call data` can be copied and passed as parameter to this script.

The following env variables are required:

- `RELAY_WS_ADDRESS`: The endpoint address of the relaychain.
- `PROVIDER_WS_ADDRESS`: The endpoint address of the DIP provider chain.
- `CONSUMER_WS_ADDRESS`: The endpoint address of the consumer chain on which DIP is to be used.
- `SUBMITTER_ADDRESS`: The address (encoded with the target chain network prefix `38`) that is authorized to submit the transaction on the target chain.
- `ENCODED_CALL`: The HEX-encoded call to DID-sign.
- `DID_URI`: The URI of the DID authorizing the operation
- `VERIFICATION_METHOD`: The verification method of the DID key to use. Because this script is not able to automatically derive the DID key required to sign the call on the target chain, it has to be explicitely set with this variable. Example values are `authentication`, `assertionMethod`, and `capabilityDelegation`.

The following optional env variables can be passed:

- `IDENTITY_DETAILS`: The runtime type definition of the identity details stored on the consumer chain, according to the DIP protocol. It defaults to `Option<u128>`, which represents a simple (optional) nonce value.
- `ACCOUNT_ID`: The runtime type definition of account address on the consumer chain. It defaults to `AccountId32`, which is the default of most Substrate-based chains. Some chains might use `AccountId20`.
- `INCLUDE_WEB3NAME`: Wether the web3name of the DID should be added to the DIP proof of not. Values can be anything that is truthy in JS terms. It defaults to `false`. **The proof generation will fail if this value is `true` but the DID does not have a web3name.**
- `DIP_PROOF_VERSION`: The version of the DIP proof to generate and use as part of the extrinsic. It defaults to `0`.

As with DID creation, there is no strong requirement on what other variables must be set.
Depending on the expected key to be used to sign the call, the right mnemonic or the right base mnemonic + derivation path must be provided.

For instance, if a call requires a DID authentication key, either `AUTH_MNEMONIC` or `DID_MNEMONIC` and `AUTH_DERIVATION_PATH` must be specified.
If a call requires a DID assertion method key, either `ATT_MNEMONIC` or `DID_MNEMONIC` and `ATT_DERIVATION_PATH` must be specified.

To run this script, execute `yarn dip-sign:sibling` and then copy the generated signature and block number to be submitted via [PolkadotJS Apps][polkadot-apps] as part of the DIP tx submission process, using the account specified in `SUBMITTER_ADDRESS`.

## Generate a DIP signature for the parent relaychain with a DID key

This script signs any valid HEX-encoded call of the parent relaychain with the right key re-generated from the provided seedling information, i.e., either with the provided mnemonic, or with the provided combination of base mnemonic and derivation path.

Valid HEX-encoded calls can be generated by interacting with [PolkadotJS Apps][polkadot-apps] under the `Developer > Extrinsics` menu.
Once the right call (i.e., the right pallet and right method) with the right parameters has been specified, the HEX-encoded value under `encoded call data` can be copied and passed as parameter to this script.

The following env variables are required:

- `RELAY_WS_ADDRESS`: The endpoint address of the relaychain.
- `PROVIDER_WS_ADDRESS`: The endpoint address of the DIP provider chain.
- `SUBMITTER_ADDRESS`: The address (encoded with the target chain network prefix `38`) that is authorized to submit the transaction on the target chain.
- `ENCODED_CALL`: The HEX-encoded call to DID-sign.
- `DID_URI`: The URI of the DID authorizing the operation
- `VERIFICATION_METHOD`: The verification method of the DID key to use. Because this script is not able to automatically derive the DID key required to sign the call on the target chain, it has to be explicitely set with this variable. Example values are `authentication`, `assertionMethod`, and `capabilityDelegation`.

The following optional env variables can be passed:

- `IDENTITY_DETAILS`: The runtime type definition of the identity details stored on the consumer chain, according to the DIP protocol. It defaults to `Option<u128>`, which represents a simple (optional) nonce value.
- `ACCOUNT_ID`: The runtime type definition of account address on the consumer chain. It defaults to `AccountId32`, which is the default of most Substrate-based chains. Some chains might use `AccountId20`.
- `INCLUDE_WEB3NAME`: Wether the web3name of the DID should be added to the DIP proof of not. Values can be anything that is truthy in JS terms. It defaults to `false`. **The proof generation will fail if this value is `true` but the DID does not have a web3name.**
- `DIP_PROOF_VERSION`: The version of the DIP proof to generate and use as part of the extrinsic. It defaults to `0`.

As with DID creation, there is no strong requirement on what other variables must be set.
Depending on the expected key to be used to sign the call, the right mnemonic or the right base mnemonic + derivation path must be provided.

For instance, if a call requires a DID authentication key, either `AUTH_MNEMONIC` or `DID_MNEMONIC` and `AUTH_DERIVATION_PATH` must be specified.
If a call requires a DID assertion method key, either `ATT_MNEMONIC` or `DID_MNEMONIC` and `ATT_DERIVATION_PATH` must be specified.

To run this script, execute `yarn dip-sign:parent` and then copy the generated signature and block number to be submitted via [PolkadotJS Apps][polkadot-apps] as part of the DIP tx submission process, using the account specified in `SUBMITTER_ADDRESS`.

## Change a DID key

The following env variables are required:
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"att-key-set": "ts-node src/att-key-set.ts",
"del-key-set": "ts-node src/del-key-set.ts",
"did-create": "ts-node src/did-create.ts",
"dip-sign:parent": "ts-node src/dip-parent-sign.ts",
"dip-sign:sibling": "ts-node src/dip-sibling-sign.ts",
"call-authorize": "ts-node src/call-sign",
"check-ts": "tsc src/** --skipLibCheck --noEmit",
"lint": "eslint . --ext .ts --format=codeframe",
Expand All @@ -15,7 +17,7 @@
"fix": "yarn lint:fix && yarn style:fix"
},
"dependencies": {
"@kiltprotocol/sdk-js": "^0.32.0",
"@kiltprotocol/sdk-js": "0.33.2-dip-2",
"dotenv": "^16.0.1"
},
"devDependencies": {
Expand All @@ -27,5 +29,6 @@
"prettier": "^2.7.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.4"
}
}
},
"packageManager": "[email protected]"
}
122 changes: 122 additions & 0 deletions src/dip-parent-sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import 'dotenv/config'

import * as Kilt from '@kiltprotocol/sdk-js'
import { ApiPromise, WsProvider } from '@polkadot/api'
import { dipProviderCalls, types } from '@kiltprotocol/type-definitions'
import { cryptoWaitReady } from '@polkadot/util-crypto'

import * as utils from './utils'

async function main() {
const relayWsAddress = process.env[utils.envNames.relayWsAddress]
const providerWsAddress = process.env[utils.envNames.providerWsAddress]
if (relayWsAddress === undefined) {
throw new Error(
`No ${utils.envNames.relayWsAddress} env variable specified.`
)
}
if (providerWsAddress === undefined) {
throw new Error(
`No ${utils.envNames.providerWsAddress} env variable specified.`
)
}
const submitterAddress = process.env[
utils.envNames.submitterAddress
] as Kilt.KiltAddress
if (submitterAddress === undefined) {
throw new Error(
`No "${utils.envNames.submitterAddress}" env variable specified.`
)
}

await cryptoWaitReady()
// eslint-disable-next-line max-len
const authKey =
utils.generateAuthenticationKey() ??
Kilt.Utils.Crypto.makeKeypairFromUri('//Alice')
const assertionKey = utils.generateAttestationKey()
const delegationKey = utils.generateDelegationKey()

const didUri = process.env[utils.envNames.didUri] as Kilt.DidUri
if (didUri === undefined) {
throw new Error(`"${utils.envNames.didUri}" not specified.`)
}

const consumerApi = await ApiPromise.create({
provider: new WsProvider(relayWsAddress),
})

const encodedCall = process.env[utils.envNames.encodedCall]
const decodedCall = consumerApi.createType('Call', encodedCall)

const [requiredKey, verificationMethod] = (() => {
const providedMethod = utils.parseVerificationMethod()
switch (providedMethod) {
case 'authentication':
return [authKey, providedMethod]
case 'assertionMethod':
return [assertionKey, providedMethod]
case 'capabilityDelegation':
return [delegationKey, providedMethod]
}
})()
if (requiredKey === undefined) {
throw new Error(
'The DID key to authorize the operation is not part of the DID Document. Please add such a key before re-trying.'
)
}

// eslint-disable-next-line max-len
const dipProofVersion = (() => {
if (process.env[utils.envNames.dipProofVersion] !== undefined) {
return parseInt(process.env[utils.envNames.dipProofVersion] as string)
} else {
return utils.defaults.dipProofVersion
}
})()

const providerApi = await ApiPromise.create({
provider: new WsProvider(providerWsAddress),
runtime: dipProviderCalls,
types,
})
const didKeyId = utils.computeDidKeyId(
providerApi,
requiredKey.publicKey,
requiredKey.type
)

const includeWeb3Name =
process.env[utils.envNames.includeWeb3Name]?.toLowerCase() === 'true' ||
utils.defaults.includeWeb3Name
const signedExtrinsic = await utils.generateParentDipTx(
consumerApi,
providerApi,
didUri,
decodedCall,
submitterAddress,
didKeyId,
verificationMethod,
includeWeb3Name,
dipProofVersion,
utils.getKeypairTxSigningCallback(requiredKey)
)

const encodedOperation = signedExtrinsic.toHex()
console.log(
`
DIP tx: ${encodedOperation}.
Please add these details to the "dipConsumer.dispatchAs" function in PolkadotJS.
`
)
console.log(
`Direct link: ${utils.generatePolkadotJSLink(
relayWsAddress,
encodedOperation
)}`
)
}

main()
.catch((e) => console.error(e))
.then(() => process.exit(0))
130 changes: 130 additions & 0 deletions src/dip-sibling-sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import 'dotenv/config'

import * as Kilt from '@kiltprotocol/sdk-js'
import { ApiPromise, WsProvider } from '@polkadot/api'
import { dipProviderCalls, types } from '@kiltprotocol/type-definitions'
import { cryptoWaitReady } from '@polkadot/util-crypto'

import * as utils from './utils'

async function main() {
const relayWsAddress = process.env[utils.envNames.relayWsAddress]
const providerWsAddress = process.env[utils.envNames.providerWsAddress]
const consumerWsAddress = process.env[utils.envNames.consumerWsAddress]
if (relayWsAddress === undefined) {
throw new Error(
`No ${utils.envNames.relayWsAddress} env variable specified.`
)
}
if (providerWsAddress === undefined) {
throw new Error(
`No ${utils.envNames.providerWsAddress} env variable specified.`
)
}
if (consumerWsAddress === undefined) {
throw new Error(
`No ${utils.envNames.consumerWsAddress} env variable specified.`
)
}

const submitterAddress = process.env[
utils.envNames.submitterAddress
] as Kilt.KiltAddress
if (submitterAddress === undefined) {
throw new Error(
`No "${utils.envNames.submitterAddress}" env variable specified.`
)
}

await cryptoWaitReady()
// eslint-disable-next-line max-len
const authKey =
utils.generateAuthenticationKey() ??
Kilt.Utils.Crypto.makeKeypairFromUri('//Alice')
const assertionKey = utils.generateAttestationKey()
const delegationKey = utils.generateDelegationKey()

const didUri = process.env[utils.envNames.didUri] as Kilt.DidUri
if (didUri === undefined) {
throw new Error(`"${utils.envNames.didUri}" not specified.`)
}

const consumerApi = await ApiPromise.create({
provider: new WsProvider(consumerWsAddress),
})

const encodedCall = process.env[utils.envNames.encodedCall]
const decodedCall = consumerApi.createType('Call', encodedCall)

const [requiredKey, verificationMethod] = (() => {
const providedMethod = utils.parseVerificationMethod()
switch (providedMethod) {
case 'authentication':
return [authKey, providedMethod]
case 'assertionMethod':
return [assertionKey, providedMethod]
case 'capabilityDelegation':
return [delegationKey, providedMethod]
}
})()
if (requiredKey === undefined) {
throw new Error(
'The DID key to authorize the operation is not part of the DID Document. Please add such a key before re-trying.'
)
}

// eslint-disable-next-line max-len
const dipProofVersion = (() => {
if (process.env[utils.envNames.dipProofVersion] !== undefined) {
return parseInt(process.env[utils.envNames.dipProofVersion] as string)
} else {
return utils.defaults.dipProofVersion
}
})()

const providerApi = await ApiPromise.create({
provider: new WsProvider(providerWsAddress),
runtime: dipProviderCalls,
types,
})
const didKeyId = utils.computeDidKeyId(
providerApi,
requiredKey.publicKey,
requiredKey.type
)

const includeWeb3Name =
process.env[utils.envNames.includeWeb3Name]?.toLowerCase() === 'true' ||
utils.defaults.includeWeb3Name
const signedExtrinsic = await utils.generateSiblingDipTx(
await ApiPromise.create({ provider: new WsProvider(relayWsAddress) }),
providerApi,
consumerApi,
didUri,
decodedCall,
submitterAddress,
didKeyId,
verificationMethod,
includeWeb3Name,
dipProofVersion,
utils.getKeypairTxSigningCallback(requiredKey)
)

const encodedOperation = signedExtrinsic.toHex()
console.log(
`
DIP tx: ${encodedOperation}.
Please add these details to the "dipConsumer.dispatchAs" function in PolkadotJS.
`
)
console.log(
`Direct link: ${utils.generatePolkadotJSLink(
consumerWsAddress,
encodedOperation
)}`
)
}

main()
.catch((e) => console.error(e))
.then(() => process.exit(0))
Loading
Loading