owned this note
owned this note
Published
Linked with GitHub
# DID Interfaces v1.0
## Transaction builder functions
### Common
#### 'Build' methods
These methods are returned from each of the functions that are responsible for handling DID-related functionality that requires submitting transactions to the KILT blockchain.
They control the outcome of the function, with 2 main options:
1. __Submit directly__. This requests all necessary changes to the state of the KILT chain and awaits the request's successful execution.
2. __Build transaction__. Only produces a transaction (signed or unsigned) and returns it. Additional client logic is responsible for submitting it to a node on the KILT network for inclusion in a block.
Additional methods could be added for related functionality, e.g., evaluating the result of the transaction execution, getting fee estimates, or performing a dry-run of the transaction before submission.
```typescript
export interface TransactionResult {
status: 'confirmed' | 'failed' | 'rejected' | 'unknown';
// these are getters that would throw if status is not as expected
asConfirmed: {
txHash: HexString;
signers: SignerInterface[];
didDocument: DidDocument;
block: { hash: HexString; number: BigInt };
events: IEvent[];
};
asFailed: {
error: Error;
txHash: HexString;
signers: SignerInterface[];
didDocument?: DidDocument;
block: { hash: HexString; number: BigInt };
events: IEvent[];
};
asRejected: {
error: Error;
txHash: HexString;
signers: SignerInterface[];
didDocument?: DidDocument;
};
asUnknown: {
error: Error;
txHash: HexString;
};
// we may or may not add these, given that you can also disambiguate based on the status
isConfirmed: boolean
isFailed: boolean;
isRejected: boolean;
isUnknown: boolean;
}
interface TransactionHandlers {
/**
* Submits a transaction for inclusion in a block, resulting in its execution in the blockchain runtime.
*
* @param options Options map to allow for named arguments.
* @param options.awaitFinalized If set to true, this waits for finalization of the block of inclusion before returning.
* @param options.submitterAccount Sets or overrides the account which produces the outer signature on the transaction and thus covers submission fees.
* @returns A Promise resolving to the DID document and info on the success of the transaction.
*/
submit(options?: {
awaitFinalized?: boolean // default: false
timeout?: number // in seconds
}): Promise<TransactionResult>
/**
* Produces a transaction that can be submitted to a blockchain node for inclusion, or signed and submitted by an external service.
* @param options Options map to allow for named arguments.
* @param options.signSubmittable If set to true, this signs the transaction with the submitterAccount, which covers transaction fees.
* In this case, if no signer is available for this account, the function throws.
* @param options.submitterAccount Sets or overrides the account which produces the outer signature on the transaction and thus covers submission fees.
* @returns A Promise resolving to an Extrinsic object (encoded transaction).
*/
getSubmittable(options?: {
signSubmittable?: boolean // default: true
}): Promise<{
txHex: HexString,
/**
* Takes info on the submission/inclusion of the transaction and evaluates whether it executed successfully.
* @param result A combination of the final transaction hash (which changes upon signing!) and the hash of the block in which the transaction was included;
* or constructor parameters for a {@link SubmittableResult}.
* @returns An object informing on the status and success of the transaction.
*/
checkResult(
result: { blockHash: HexString; txHash: HexString } | SubmittableResultValue
): Promise<TransactionResult>
}>
}
```
##### Notes
- We may want to add a DID nonce parameter on submit & getSubmittable to address parallelization issues; however, for now, parallelization is out of scope.
#### Shared Arguments
```typescript
/** base58 encoded bytes, using the bitcoin alphabet */
type Base58Btc = string
/** multibase encoding of a public- or private key including multicodec variant flag */
type KeyMultibaseEncoded = `z${Base58Btc}`
type SharedArguments = {
didDocument: DidDocument
api: ApiPromise
signers: Array<
| SignerInterface
| KeyringPair
| {
secretKeyMultibase: KeyMultibaseEncoded
publicKeyMultibase: KeyMultibaseEncoded
}
>
submitterAccount: KiltAddress
}
type AcceptedPublicKeyEncodings =
| KeyMultibaseEncoded
| { publicKeyMultibase: KeyMultibaseEncoded }
| Pick<KeyringPair, 'publicKey' | 'type'> // interface allows KeyringPair too
```
### DID-authorize
```typescript
/**
* Instructs a transaction (state transition) as this DID (with this DID as the origin).
* @param options.call The transaction / call to execute.
*/
export declare function transact(
options: SharedArguments & {
call: Call
}
): TransactionHandlers
```
### Create a DID
```typescript
/**
* Creates an on-chain DID based on an authentication key.
* @param options.fromPublicKey The public key that will feature as the DID's initial authentication method and will determine the DID identifier.
*/
export declare function createDid(
options: Omit<SharedArguments, 'didDocument'> & {
fromPublicKey: AcceptedPublicKeyEncodings
}
): TransactionHandlers
```
#### Example Usage
```typescript
// keypair generation TBD; but there would be an easy-to-use helper that generates a multikey encoded pair
const keypair = generateKeypair("my mnemonic with many words")
// most common usage
const {didDocument, error, status} = await DidHelper.createDid({
api
signers: [keypair],
submitterAccount: "4abcdef",
fromPublicKey: keypair
}).submit()
```
### Managing Verification Methods
```typescript
type VerificationRelationship =
"authentication" |
"assertionMethod" |
"capabilityDelegation" |
"keyAgreement"
/**
* Replaces all existing verification methods for the selected `relationship` with `publicKey`.
* @param options.publicKey The public key to be used for this verification method.
* @param options.relationship The relationship for which this verification method shall be useable.
*/
export declare function setVerificationMethod(
options: SharedArguments & {
publicKey: AcceptedPublicKeyEncodings
relationship: VerificationRelationship
}
): TransactionHandlers
```
#### Notes
- Removal is out of scope for now, as is having multiple key agreement keys.
- For removal a `null` or `UNSET` type for `setVerificationMethod` could be introduced, but not in the first version.
### Managing Services
```typescript
/**
* Adds a service to the DID Document.
* @param options.service The service entry to add to the document.
* If the service id is relative (begins with #) it is automatically expanded with the DID taken from didDocument.id.
*/
export declare function addService(
options: SharedArguments & {
service: Service<DidUrl | UriFragment>
}
): TransactionHandlers
/**
* Removes a service from the DID Document.
* @param options.id The id of the service to remove from the document.
* If the service id is relative (begins with #) it is automatically expanded with the DID taken from didDocument.id.
*/
export declare function removeService(
options: SharedArguments & {
id: DidUrl | UriFragment
}
): TransactionHandlers
```
### Managing w3n
```typescript
/**
* Adds a w3n nickname to the DID Document.
* @param options.name The name to be claimed.
* Must be still available (not yet claimed by another DID) for this operation to succeed.
*/
export declare function claimWeb3Name(
options: SharedArguments & {
name: string
}
): TransactionHandlers
/**
* Removes w3n nickname from the DID Document, allowing it to be claimed by others.
*/
export declare function releaseWeb3Name(
options: SharedArguments
): TransactionHandlers
```
## Get signer
Picks and returns a signer for a given operation. Also helps creating the required signer from key pairs or other signers.
```typescript
/**
* Selects and returns a DID signer for a given purpose and algorithm.
* @param options.signers Signers from which to choose from; can also be `KeyringPair` instances or other key pair representations.
* @param options.relationship Which verification relationship the key should have to the DID.
* Defaults to `authentication`.
* @param options.algorithm Optionally filter signers by algorithm(s).
*/
export declare function selectSigner(
Pick<SharedArguments, 'didDocument' | 'signers'> & {
relationship?: string // defaults to authentication
algorithm?: string | string[]
}): Promise<SignerInterface>
```
#### Notes
- This function does not do much in this variant - it only produces signers for your given DID from the signers/keys you fed into it (which comprises matching to VMs and possibly turning keypairs into signers).
- In an aliased version on a DidHelper instance, this would be more helpful, where did document and signers are already pinned.
- Function is asyncronous because signer creation functions are.
## Examples
### standard case
```typescript
const submitterAccount = "4abcdef..."
let {didDocument, signers} = (
await DidHelper.createDid({
api
signers: [keypair],
submitterAccount,
fromPublicKey: keypair
}).submit()
).asConfirmed;
{didDocument, signers} = (
await DidHelper.setVerificationMethod({
api
signers,
submitterAccount,
didDocument,
publicKey: keypair,
relationship: 'assertionMethod'
}
).submit()
).asConfirmed;
{didDocument} = (
await DidHelper.claimWeb3Name({
api
signers,
submitterAccount,
didDocument,
name: 'the_dork'
}).submit()
).asConfirmed;
```
### using txd or other means of submitting
```typescript
import { submitViaTxd } from './txdHelpers.ts'
const submitterAccount = "4abcdef..."
const didCreate = await DidHelper.createDid({
api
signers: [keypair],
submitterAccount,
fromPublicKey: keypair
}).getSubmittable()
let {blockHash, txHash} = await submitViaTxd(
didCreate.txHex
)
let {didDocument, signers} = (await didCreate.checkResult(
{blockHash, txHash}
)).asConfirmed
const vmSet = await DidHelper.setVerificationMethod({
api
signers,
submitterAccount,
didDocument,
publicKey: keypair,
relationships: {
assertionMethod: true
}
}).getSubmittable()
{blockHash, txHash} = await submitViaTxd(
vmSet.txHex
)
{didDocument, signers} = (await vmSet.checkResult(
{blockHash, txHash}
)).asConfirmed
const w3nClaim = await DidHelper.claimWeb3Name({
api
signers,
submitterAccount,
didDocument,
name: 'the_dork'
}).getSubmittable()
{blockHash, txHash} = await submitViaTxd(
w3nClaim.txHex
)
{didDocument} = (await w3nClaim.checkResult(
{blockHash, txHash}
)).asConfirmed
```
## Integration with issue / createPresentation
All `submit()` operations as well as the pinning / instance logic (see below) provide all arguments that are required for the `issuer`/`holder` parameter of `issue` and `createPresentation`.
Thus, these functions could interact as follows:
```typescript
const issuer = pin({
api,
didDocument,
signers: [myKeypair],
submitterAccount,
})
const signedCredential = await issue({
credential, issuer
})
```
#### Notes
- Functions do not, however, provide the authorize/submit strategies as parameters, so I'd expect this part of the issue function to change.
## Extensions / aliases
Things that can be added at some point for more convenience and ease of use.
- DidHelper instance allowing to set `api`, `signers`, `submitterAccount`, and `didDocument`, as well as options to `.submit()` and `.getSubmittable()`. Has methods aliasing the functions above which use these settings as defaults.
- batching, where multiple function calls are passed as parameters to a `batch()` function
### Instance
```typescript
type PinnableArgs = SharedArguments & {
awaitFinalized?: boolean
timeout?: number
signSubmittable?: boolean
}
/**
* Returns a set of aliased versions of the functions above, where all `PinneableArgs` can be omitted and default to the values passed to this function.
*/
export declare function pin(args: PinneableArgs): {
config: PinneableArgs,
getSigner,
transact,
createDid,
...
}
```
### Batching
Enables a syntax along the lines of
```typescript
const pinned = pin({
didDocument,
api,
signers,
submitterAccount,
timeout: 30
})
const {didDocument, signers} = (await pinned.batch(
pinned.createDid({fromPublicKey: 'zABCmyKey123'}),
pinned.setVerificationMethod({publicKey: 'zABCmyKey123'}),
pinned.claimWeb3Name({name: 'john_doe'})
).submit()).asConfirmed
```
This can either work by the batch function calling `getSubmittable` on each result (which would likely have to accept additional parameters, such as the DID nonce to be used). In this case, each transaction would be DID-signed individually, then wrapped by a `batchAll`.
Alternatively it could make use of an additional finalization method introduced for this purpose (say, `_batch()`), which accepts all relevant arguments and returns a non-wrapped/authorized call which is added to a `batchAll` and then DID-signed.