Skip to content

fix: unsigned submittable #890

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 9 commits into from
Jul 24, 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
60 changes: 58 additions & 2 deletions packages/sdk-js/src/DidHelpers/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@

import { Blockchain } from '@kiltprotocol/chain-helpers'
import { multibaseKeyToDidKey } from '@kiltprotocol/did'

import type {
KeyringPair,
KiltAddress,
MultibaseKeyPair,
DidHelpersAcceptedPublicKeyEncodings,
SharedArguments,
TransactionHandlers,
TransactionSigner,
} from '@kiltprotocol/types'
import { Keyring, Multikey, Crypto } from '@kiltprotocol/utils'

export async function submitImpl(
getSubmittable: TransactionHandlers['getSubmittable'],
Expand All @@ -24,7 +28,10 @@ export async function submitImpl(
awaitFinalized?: boolean
}
): ReturnType<TransactionHandlers['submit']> {
const submittable = await getSubmittable(options)
const submittable = await getSubmittable({
...options,
signSubmittable: true,
})

const { awaitFinalized = true } = options
const result = await Blockchain.submitSignedTx(
Expand Down Expand Up @@ -63,3 +70,52 @@ export function convertPublicKey(pk: DidHelpersAcceptedPublicKeyEncodings): {
}
return { publicKey, type }
}

export function extractSubmitterSignerAndAccount(
submitter: KeyringPair | TransactionSigner | MultibaseKeyPair | KiltAddress
): {
submitterSigner?: TransactionSigner | KeyringPair | undefined
submitterAccount: KiltAddress
} {
// KiltAddress
if (typeof submitter === 'string') {
return {
submitterAccount: submitter,
}
}
// KeyringPair
if ('address' in submitter) {
return {
submitterAccount: submitter.address as KiltAddress,
submitterSigner: submitter,
}
}
// Blockchain.TransactionSigner
if ('id' in submitter) {
return {
submitterAccount: submitter.id as KiltAddress,
submitterSigner: submitter,
}
}
// MultibaseKeyPair
if ('publicKeyMultibase' in submitter) {
const submitterAccount = Crypto.encodeAddress(
Multikey.decodeMultibaseKeypair(submitter).publicKey,
38
)
const keypair = Multikey.decodeMultibaseKeypair(submitter)
if (keypair.type === 'x25519') {
throw new Error('x25519 keys are not supported')
}
const keyring = new Keyring().createFromPair(
keypair,
undefined,
keypair.type === 'secp256k1' ? 'ecdsa' : keypair.type
)
return {
submitterAccount,
submitterSigner: keyring,
}
}
throw new Error('type of submitter is invalid')
}
19 changes: 12 additions & 7 deletions packages/sdk-js/src/DidHelpers/createDid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ import {
} from '@kiltprotocol/did'
import type {
DidHelpersAcceptedPublicKeyEncodings,
KiltAddress,
SharedArguments,
SignerInterface,
TransactionHandlers,
} from '@kiltprotocol/types'
import { Crypto, Signers } from '@kiltprotocol/utils'
import { checkResultImpl } from './checkResult.js'

import { convertPublicKey, submitImpl } from './common.js'
import {
convertPublicKey,
extractSubmitterSignerAndAccount,
submitImpl,
} from './common.js'

function implementsSignerInterface(input: any): input is SignerInterface {
return 'algorithm' in input && 'id' in input && 'sign' in input
Expand All @@ -44,15 +47,14 @@ export function createDid(
submitOptions = {}
) => {
const { fromPublicKey, submitter, signers, api } = options
const { signSubmittable = true } = submitOptions
const { signSubmittable = false } = submitOptions
const { publicKey, type } = convertPublicKey(fromPublicKey)

if (!signingMethodTypes.includes(type)) {
throw new Error(`unknown key type ${type}`)
}
const submitterAccount = (
'address' in submitter ? submitter.address : submitter.id
) as KiltAddress
const { submitterSigner, submitterAccount } =
extractSubmitterSignerAndAccount(submitter)

const accountSigners = (
await Promise.all(
Expand All @@ -77,7 +79,10 @@ export function createDid(
)

if (signSubmittable) {
didCreation = await Blockchain.signTx(didCreation, submitter)
if (typeof submitterSigner === 'undefined') {
throw new Error('submitter does not include a secret key')
}
didCreation = await Blockchain.signTx(didCreation, submitterSigner)
}

return {
Expand Down
24 changes: 23 additions & 1 deletion packages/sdk-js/src/DidHelpers/transact.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('transact', () => {
expectedEvents: [
{ section: 'attestation', method: 'AttestationCreated' },
],
}).getSubmittable({ signSubmittable: false })
}).getSubmittable()

expect(txHex).toContain('0x')
const parsed = mockedApi.tx(txHex)
Expand Down Expand Up @@ -118,4 +118,26 @@ describe('transact', () => {
value: confirmed.toJSON(),
})
})

it('creates an unsigned submittable', async () => {
const { txHex } = await transact({
didDocument,
api: mockedApi,
submitter: keypair.address,
signers: [keypair],
call: mockedApi.tx.attestation.add(
new Uint8Array(32).fill(1),
new Uint8Array(32).fill(1),
null
),
expectedEvents: [
{ section: 'attestation', method: 'AttestationCreated' },
],
}).getSubmittable({ signSubmittable: false })

expect(txHex).toContain('0x')
const parsed = mockedApi.tx(txHex)
expect(parsed.method).toHaveProperty('section', 'did')
expect(parsed.method).toHaveProperty('method', 'submitDidCall')
})
})
21 changes: 10 additions & 11 deletions packages/sdk-js/src/DidHelpers/transact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@
*/

import type { Extrinsic } from '@polkadot/types/interfaces'

import { Blockchain } from '@kiltprotocol/chain-helpers'
import { authorizeTx, signersForDid } from '@kiltprotocol/did'
import type {
KiltAddress,
SharedArguments,
SubmittableExtrinsic,
SharedArguments,
TransactionHandlers,
} from '@kiltprotocol/types'

import { extractSubmitterSignerAndAccount, submitImpl } from './common.js'
import { checkResultImpl } from './checkResult.js'
import { submitImpl } from './common.js'

/**
* Instructs a transaction (state transition) as this DID (with this DID as the origin).
Expand All @@ -35,7 +32,7 @@ export function transactInternal(
const getSubmittable: TransactionHandlers['getSubmittable'] = async (
submitOptions:
| {
signSubmittable?: boolean // default: true
signSubmittable?: boolean // default: false
didNonce?: number | BigInt
}
| undefined = {}
Expand All @@ -48,13 +45,12 @@ export function transactInternal(
api,
expectedEvents,
} = options
const { didNonce, signSubmittable = true } = submitOptions
const { didNonce, signSubmittable = false } = submitOptions
const call = await callFactory()
const didSigners = await signersForDid(didDocument, ...signers)

const submitterAccount = (
'address' in submitter ? submitter.address : submitter.id
) as KiltAddress
const { submitterSigner, submitterAccount } =
extractSubmitterSignerAndAccount(submitter)

let authorized: SubmittableExtrinsic = await authorizeTx(
didDocument,
Expand All @@ -69,7 +65,10 @@ export function transactInternal(
)

if (signSubmittable) {
authorized = await Blockchain.signTx(authorized, submitter)
if (typeof submitterSigner === 'undefined') {
throw new Error('submitter does not include a secret key')
}
authorized = await Blockchain.signTx(authorized, submitterSigner)
}

return {
Expand Down
8 changes: 4 additions & 4 deletions packages/types/src/Address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ export type KiltAddress = KiltKeyringPair['address']
declare module '@polkadot/keyring' {
function encodeAddress(
key: HexString | Uint8Array | string,
ss58Format?: Prefix
): string
ss58Format: 38
): KiltAddress
function encodeAddress(
key: HexString | Uint8Array | string,
ss58Format?: 38
): KiltAddress
ss58Format?: Prefix
): string
}
16 changes: 7 additions & 9 deletions packages/types/src/DidHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import type { ApiPromise } from '@polkadot/api'
import type { SubmittableResultValue } from '@polkadot/api/types'
import type { GenericEvent } from '@polkadot/types'
import type { Multikey } from '@kiltprotocol/utils'
import type { DidDocument } from './Did'
import type {
ApiPromise,
GenericEvent,
HexString,
KeyringPair,
SubmittableResultValue,
} from './Imported'
import type {
MultibaseKeyPair,
MultibasePublicKey,
SignerInterface,
TransactionSigner,
} from './Signers'
import { HexString, KeyringPair } from './Imported.js'
import { DidDocument } from './Did.js'
import { KiltAddress } from './Address.js'

export interface TransactionResult {
status: 'confirmed' | 'failed' | 'rejected' | 'unknown'
Expand Down Expand Up @@ -105,7 +103,7 @@ export type SharedArguments = {
didDocument: DidDocument
api: ApiPromise
signers: DidHelpersAcceptedSigners[]
submitter: KeyringPair | TransactionSigner
submitter: KeyringPair | TransactionSigner | MultibaseKeyPair | KiltAddress
}

type PublicKeyAndType = {
Expand Down
19 changes: 19 additions & 0 deletions tests/integration/didHelpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,25 @@ describe('transact', () => {
Verifier.verifyCredential({ credential: issued })
).resolves.toMatchObject({ verified: true })
}, 30_000)

it('creates signed submittable', async () => {
const serialized = CType.toChain(cType)
const call = api.tx.ctype.add(serialized)

const { txHex } = await DidHelpers.transact({
api,
signers: [keypair],
submitter: paymentAccount,
didDocument,
call,
expectedEvents: [{ section: 'ctype', method: 'CTypeCreated' }],
}).getSubmittable({ signSubmittable: true })
expect(txHex).toContain('0x')
const parsed = api.tx(txHex)
expect(parsed.method).toHaveProperty('section', 'did')
expect(parsed.method).toHaveProperty('method', 'submitDidCall')
expect(parsed.isSigned).toBe(true)
}, 30_000)
})

afterAll(async () => {
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const devBob = Crypto.makeKeypairFromUri('//Bob')
export const devCharlie = Crypto.makeKeypairFromUri('//Charlie')

export function addressFromRandom(): KiltAddress {
return encodeAddress(randomAsU8a())
return encodeAddress(randomAsU8a(), 38)
}

export async function isCtypeOnChain(cType: ICType): Promise<boolean> {
Expand Down