From 0038f5f29ef64575f8e5278e773cde1b0e22389d Mon Sep 17 00:00:00 2001 From: Vincent Tse Date: Wed, 20 Sep 2023 15:04:28 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=91=8C=20IMPROVE:=20Simplify=20the=20sign?= =?UTF-8?q?=20transactions=20api=20to=20make=20it=20more=20easy=20to=20use?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/actions/sign-transactions.ts | 11 +--- src/lib/crypto.ts | 90 +++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/lib/actions/sign-transactions.ts b/src/lib/actions/sign-transactions.ts index 14c7a3b..0e96618 100644 --- a/src/lib/actions/sign-transactions.ts +++ b/src/lib/actions/sign-transactions.ts @@ -1,17 +1,12 @@ import { getAddress, getCurrentAccount, privateKey } from '../account' import connector from '../connector' -import { signTransaction } from '../crypto' +import { signTransaction, signTransactions } from '../crypto' export async function process(params: any, host: string) { const wif = await getCurrentAccount().then((account) => privateKey.value) const signingTransactions = params.transactions + const signedTransactions = signTransactions(wif, signingTransactions) - const signatures = signingTransactions.map((transaction: any) => { - const signature = signTransaction(wif, transaction) - - return signature - }) - - return { signatures } + return { signedTransactions } } diff --git a/src/lib/crypto.ts b/src/lib/crypto.ts index a9912e7..4b5a789 100644 --- a/src/lib/crypto.ts +++ b/src/lib/crypto.ts @@ -66,23 +66,16 @@ export const verifySignature = ( return { verified } } +type ToSignTransaction = { + txHex: string + scriptHex: string + inputIndex: number + satoshis: number + sigtype?: number +} export const signTransaction = ( wif: string, - { - txHex, - scriptHex, - address, - inputIndex, - satoshis, - sigtype, - }: { - txHex: string - scriptHex: string - address?: string - inputIndex: number - satoshis: number - sigtype?: number - }, + { txHex, scriptHex, inputIndex, satoshis, sigtype }: ToSignTransaction, returnsTransaction: boolean = false ) => { if (!sigtype) sigtype = mvc.crypto.Signature.SIGHASH_ALL | mvc.crypto.Signature.SIGHASH_FORKID @@ -120,3 +113,70 @@ export const signTransaction = ( txid: tx.id, } } + +export const signTransactions = (wif: string, toSignTransactions: ToSignTransaction[]) => { + const privateKey = mvc.PrivateKey.fromWIF(wif) + const publicKey = privateKey.toPublicKey() + + // find out if transactions other than the first one are dependent on previous ones + // if so, we need to sign them in order, and sequentially update the prevTxId of the later ones + // so that the signature of the previous one can be calculated correctly + const toSignTransactionsWithDependsOn: (ToSignTransaction & { + dependsOn?: number + })[] = [] + const txids = toSignTransactions.map((tx) => new mvc.Transaction(tx.txHex).id) + for (let i = 0; i < toSignTransactions.length; i++) { + const { txHex, inputIndex } = toSignTransactions[i] + const tx = new mvc.Transaction(txHex) + const prevTxId = tx.inputs[inputIndex].prevTxId.toString('hex') + if (txids.includes(prevTxId)) { + toSignTransactionsWithDependsOn.push({ + ...toSignTransactions[i], + dependsOn: txids.indexOf(prevTxId), + }) + } else { + toSignTransactionsWithDependsOn.push(toSignTransactions[i]) + } + } + + const signedTransactions: { + txHex: string + txid: string + }[] = [] + for (let i = 0; i < toSignTransactionsWithDependsOn.length; i++) { + let { txHex, scriptHex, inputIndex, satoshis, sigtype } = toSignTransactionsWithDependsOn[i] + const toSign = toSignTransactionsWithDependsOn[i] + + if (!sigtype) { + sigtype = mvc.crypto.Signature.SIGHASH_ALL | mvc.crypto.Signature.SIGHASH_FORKID + } + + const tx = new mvc.Transaction(txHex) + + // find out if this transaction depends on previous ones + // if so, we need to update the prevTxId of the current one + if (toSign.dependsOn !== undefined) { + const prevTxId = signedTransactions[toSign.dependsOn].txid + tx.inputs[inputIndex].prevTxId = Buffer.from(prevTxId, 'hex') + } + + // Build signature of this input + const signature = mvc.Transaction.Sighash.sign( + tx, + privateKey, + sigtype, + inputIndex, + new mvc.Script(scriptHex), + new BN(satoshis) + ) + const signatureScript = mvc.Script.buildPublicKeyHashIn(publicKey, signature, sigtype) + tx.inputs[inputIndex].setScript(signatureScript) + + signedTransactions.push({ + txHex: tx.toString(), + txid: tx.id, + }) + } + + return signedTransactions +}