Skip to content

refactor!: align issuer/holder interfaces #889

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

Merged
merged 6 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 3 additions & 11 deletions packages/chain-helpers/src/blockchain/Blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,22 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import type { ApiPromise } from '@polkadot/api'
import { ApiPromise, SubmittableResult } from '@polkadot/api'
import type { TxWithEvent } from '@polkadot/api-derive/types'
import type { Vec } from '@polkadot/types'
import type { Call, Extrinsic } from '@polkadot/types/interfaces'
import type { AnyNumber, IMethod } from '@polkadot/types/types'
import type { BN } from '@polkadot/util'

// eslint-disable-next-line @typescript-eslint/no-unused-vars -- doing this instead of import '@kiltprotocol/augment-api' to avoid creating an import at runtime
import type * as _ from '@kiltprotocol/augment-api'
import type {
ISubmittableResult,
KeyringPair,
KiltAddress,
SignerInterface,
SubmittableExtrinsic,
SubscriptionPromise,
TransactionSigner,
} from '@kiltprotocol/types'

import { SubmittableResult } from '@polkadot/api'

import { ConfigService } from '@kiltprotocol/config'
import { SDKErrors, Signers } from '@kiltprotocol/utils'

Expand Down Expand Up @@ -170,11 +167,6 @@ export async function submitSignedTx(

export const dispatchTx = submitSignedTx

export type TransactionSigner = SignerInterface<
'Ecrecover-Secp256k1-Blake2b' | 'Sr25519' | 'Ed25519',
KiltAddress
>

/**
* Signs a SubmittableExtrinsic.
*
Expand Down
203 changes: 106 additions & 97 deletions packages/credentials/src/V1/KiltAttestationProofV1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,17 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import type { ApiPromise } from '@polkadot/api'
import type { QueryableStorageEntry } from '@polkadot/api/types'
import type { Option, u64, Vec } from '@polkadot/types'
import type {
AccountId,
Extrinsic,
Hash,
} from '@polkadot/types/interfaces/types.js'
import type { IEventData } from '@polkadot/types/types'
import {
hexToU8a,
stringToU8a,
u8aCmp,
u8aConcatStrict,
Expand All @@ -19,67 +29,61 @@ import {
encodeAddress,
randomAsU8a,
} from '@polkadot/util-crypto'
import type { ApiPromise } from '@polkadot/api'
import type { QueryableStorageEntry } from '@polkadot/api/types'
import type { Option, u64, Vec } from '@polkadot/types'
import type {
AccountId,
EventRecord,
Extrinsic,
Hash,
} from '@polkadot/types/interfaces/types.js'
import type { IEventData } from '@polkadot/types/types'

import type {
FrameSystemEventRecord,
RuntimeCommonAuthorizationAuthorizationId,
} from '@kiltprotocol/augment-api'
import { Blockchain } from '@kiltprotocol/chain-helpers'
import { ConfigService } from '@kiltprotocol/config'
import {
authorizeTx,
fromChain,
getFullDid,
signersForDid,
validateDid,
fromChain,
} from '@kiltprotocol/did'
import { JsonSchema, SDKErrors, Caip19, Signers } from '@kiltprotocol/utils'
import { ConfigService } from '@kiltprotocol/config'
import { Blockchain } from '@kiltprotocol/chain-helpers'
import type {
FrameSystemEventRecord,
RuntimeCommonAuthorizationAuthorizationId,
} from '@kiltprotocol/augment-api'
import type {
DidDocument,
Did,
DidDocument,
HexString,
ICType,
IDelegationNode,
KiltAddress,
SignerInterface,
SharedArguments,
TransactionResult,
} from '@kiltprotocol/types'
import * as CType from '../ctype/index.js'
import { Caip19, JsonSchema, SDKErrors, Signers } from '@kiltprotocol/utils'

import { CTypeLoader } from '../ctype/CTypeLoader.js'
import * as CType from '../ctype/index.js'
import { HolderOptions } from '../interfaces.js'
import {
DEFAULT_CREDENTIAL_CONTEXTS,
validateStructure as validateCredentialStructure,
validateSubject,
} from './KiltCredentialV1.js'
import { fromGenesisAndRootHash } from './KiltRevocationStatusV1.js'
import {
jsonLdExpandCredentialSubject,
ExpandedContents,
delegationIdFromAttesterDelegation,
getDelegationNodeIdForCredential,
assertMatchingConnection,
credentialIdFromRootHash,
credentialIdToRootHash,
KILT_CREDENTIAL_IRI_PREFIX,
delegationIdFromAttesterDelegation,
ExpandedContents,
getDelegationNodeIdForCredential,
jsonLdExpandCredentialSubject,
KILT_ATTESTER_DELEGATION_V1_TYPE,
KILT_ATTESTER_LEGITIMATION_V1_TYPE,
KILT_CREDENTIAL_IRI_PREFIX,
spiritnetGenesisHash,
} from './common.js'
import { KiltRevocationStatusV1 } from './index.js'
import type {
CredentialSubject,
KiltAttestationProofV1,
KiltAttesterLegitimationV1,
KiltCredentialV1,
} from './types.js'
import { CTypeLoader } from '../ctype/CTypeLoader.js'
import { KiltRevocationStatusV1 } from './index.js'

export type Interface = KiltAttestationProofV1

Expand Down Expand Up @@ -659,42 +663,55 @@ export function finalizeProof(
}
}

export interface TransactionResult {
status: 'InBlock' | 'Finalized'
includedAt: { blockHash: Uint8Array; blockHeight?: BigInt; blockTime?: Date }
events?: EventRecord[]
interface SimplifiedTransactionResult {
block: { hash: HexString }
}

type CustomHandlers = {
authorizeTx: (tx: Extrinsic) => Promise<Extrinsic>
submitTx: (tx: Extrinsic) => Promise<TransactionResult>
}
type SignersAndSubmitter = {
submitterAccount: KiltAddress
signers: readonly SignerInterface[]
}
export type IssueOpts =
| (CustomHandlers & Partial<SignersAndSubmitter>)
| (Partial<CustomHandlers> & SignersAndSubmitter)

async function defaultTxSubmit(
tx: Extrinsic,
submitterAccount: KiltAddress,
signers: readonly SignerInterface[],
api: ApiPromise
): Promise<TransactionResult> {
const extrinsic = api.tx(tx)
const signed = extrinsic.isSigned
? extrinsic
: await extrinsic.signAsync(submitterAccount, {
signer: Signers.getPolkadotSigner(signers),
})
const result = await Blockchain.submitSignedTx(signed, {
export type IssueOpts = {
submitter:
| KiltAddress
| ((
args: Pick<SharedArguments, 'didDocument' | 'api' | 'signers'> & {
call: Extrinsic
}
) => Promise<SimplifiedTransactionResult | TransactionResult>)
} & Pick<HolderOptions, 'signers'>

async function defaultTxSubmit({
didDocument,
call,
signers,
submitter,
}: Omit<SharedArguments, 'submitter'> & {
call: Extrinsic
submitter: KiltAddress
}): Promise<SimplifiedTransactionResult> {
let extrinsic = await authorizeTx(
didDocument,
call,
await signersForDid(didDocument, ...signers),
submitter
)

if (!extrinsic.isSigned) {
const accountSigners = (
await Promise.all(
signers.map((keypair) =>
'algorithm' in keypair
? [keypair]
: Signers.getSignersForKeypair({ keypair })
)
)
).flat()
extrinsic = await extrinsic.signAsync(submitter, {
signer: Signers.getPolkadotSigner(accountSigners),
})
}
const result = await Blockchain.submitSignedTx(extrinsic, {
resolveOn: Blockchain.IS_FINALIZED,
})
const blockHash = result.status.asFinalized
const { events } = result
return { status: 'Finalized', includedAt: { blockHash }, events }
return { block: { hash: blockHash.toHex() } }
}

/**
Expand All @@ -715,7 +732,7 @@ async function defaultTxSubmit(
*/
export async function issue(
credential: Omit<UnissuedCredential, 'issuer'>,
issuer: Did | DidDocument,
issuer: DidDocument,
options: IssueOpts
): Promise<KiltCredentialV1> {
const updatedCredential = {
Expand All @@ -726,47 +743,39 @@ export async function issue(
const api = ConfigService.get('api')
const call = api.tx.attestation.add(...callArgs)

const {
signers,
submitterAccount,
authorizeTx: customAuthorizeTx,
submitTx: customSubmitTx,
...otherParams
} = options
const { signers, submitter } = options

if (
!(customAuthorizeTx && customSubmitTx) &&
!(signers && submitterAccount)
) {
throw new Error(
'`signers` and `submitterAccount` are required options if authorizeTx or submitTx are not given'
)
const args: Pick<SharedArguments, 'didDocument' | 'api' | 'signers'> & {
call: Extrinsic
} = {
didDocument: issuer,
signers,
api,
call,
}

/* eslint-disable @typescript-eslint/no-non-null-assertion -- we've checked the appropriate combination of parameters above, but typescript does not follow */
const didSigned = customAuthorizeTx
? await customAuthorizeTx(call)
: await authorizeTx(issuer, call, signers!, submitterAccount!, otherParams)

const transactionPromise = customSubmitTx
? customSubmitTx(didSigned)
: defaultTxSubmit(didSigned, submitterAccount!, signers!, api)
/* eslint-enable @typescript-eslint/no-non-null-assertion */

const {
status,
includedAt: { blockHash, blockTime },
} = await transactionPromise

if (status !== 'Finalized' && status !== 'InBlock') {
throw new SDKErrors.SDKError(
`Unexpected transaction status ${status}; the transaction should be "InBlock" or "Finalized" for issuance to continue`
)
const transactionPromise =
typeof submitter === 'function'
? submitter(args)
: defaultTxSubmit({
...args,
submitter,
})

let result = await transactionPromise
if ('status' in result) {
if (result.status !== 'confirmed') {
throw new SDKErrors.SDKError(
`Unexpected transaction status ${result.status}; the transaction should be "confirmed" for issuance to continue`
)
}
result = result.asConfirmed
}

const timestamp =
blockTime ??
new Date((await api.query.timestamp.now.at(blockHash)).toNumber())
const blockHash = hexToU8a(result.block.hash)

const timestamp = new Date(
(await (await api.at(blockHash)).query.timestamp.now()).toNumber()
)
return finalizeProof(updatedCredential, proof, {
blockHash,
timestamp,
Expand Down
9 changes: 5 additions & 4 deletions packages/credentials/src/holder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { CryptoSuite } from '@kiltprotocol/jcs-data-integrity-proofs-common

import { Did } from '@kiltprotocol/types'
import { SDKErrors } from '@kiltprotocol/utils'
import { signersForDid } from '@kiltprotocol/did'

import { KiltAttestationProofV1, KiltCredentialV1 } from './V1/index.js'
import { VerifiableCredential, VerifiablePresentation } from './V1/types.js'
Expand Down Expand Up @@ -159,13 +160,13 @@ export async function createPresentation(
now?: Date
} = {}
): Promise<VerifiablePresentation> {
const { did, didDocument, signers } = holder
const { didDocument, signers } = holder
const { validFrom, validUntil, verifier } = presentationOptions
const { proofPurpose, proofType, challenge, domain, now } = proofOptions

let presentation = await Presentation.create({
credentials,
holder: did,
holder: didDocument.id,
validFrom: validFrom ?? now,
validUntil,
verifier,
Expand All @@ -184,8 +185,8 @@ export async function createPresentation(

presentation = await DataIntegrity.signWithDid({
document: presentation,
signerDid: didDocument ?? did,
signers,
signerDid: didDocument,
signers: await signersForDid(didDocument, ...signers),
proofPurpose,
challenge,
domain,
Expand Down
23 changes: 17 additions & 6 deletions packages/credentials/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,28 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import type { SignerInterface } from '@kiltprotocol/jcs-data-integrity-proofs-common'
import type { Did, DidDocument } from '@kiltprotocol/types'
import type { Proof, VerifiableCredential } from './V1/types'
import type {
Base58BtcMultibaseString,
DidDocument,
KeyringPair,
SignerInterface,
} from '@kiltprotocol/types'

import type { IssueOpts } from './V1/KiltAttestationProofV1'
import type { Proof, VerifiableCredential } from './V1/types'

export type SecuredDocument = { proof: Proof[] | Proof }

export interface HolderOptions {
did: Did
didDocument?: DidDocument
signers: SignerInterface[]
didDocument: DidDocument
signers: Array<
| SignerInterface
| KeyringPair
| {
secretKeyMultibase: Base58BtcMultibaseString
publicKeyMultibase: Base58BtcMultibaseString
}
>
}

export type IssuerOptions = HolderOptions & IssueOpts
Expand Down
Loading
Loading