Skip to content
Open
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
1 change: 1 addition & 0 deletions modules/utxo-core/src/bip322/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './toSpend';
export * from './toSign';
export * from './utils';
export * from './verify';
53 changes: 53 additions & 0 deletions modules/utxo-core/src/bip322/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import assert from 'assert';

import * as utxolib from '@bitgo/utxo-lib';

import { buildToSpendTransaction } from './toSpend';

export type VerificationInfo = {
message: string;
address: string;
};

/**
*
* @param toSignTx
* @param verificationInfo
* @param network
*/
export function verifyFixedScriptToSignTxWithInfo(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

per convention, void funcs that throw are prefixed with assert

Suggested change
export function verifyFixedScriptToSignTxWithInfo(
export function assertValidFixedScriptToSignTxWithInfo(

toSignTx: utxolib.Transaction<bigint>,
verificationInfo: VerificationInfo[],
network: utxolib.Network
): void {
assert.deepStrictEqual(
toSignTx.ins.length,
verificationInfo.length,
'to_sign transaction must have one input per message'
);

// Verify the transaction structure
assert.deepStrictEqual(toSignTx.version, 0, 'to_sign transaction version must be 0');
assert.deepStrictEqual(toSignTx.locktime, 0, 'to_sign transaction locktime must be 0');
assert.deepStrictEqual(toSignTx.outs.length, 1, 'to_sign transaction must have one output');
assert.deepStrictEqual(toSignTx.outs[0].value, BigInt(0), 'to_sign transaction output value must be 0');
assert.deepStrictEqual(
toSignTx.outs[0].script.toString('hex'),
'6a',
'to_sign transaction output script must be OP_RETURN'
);

// Verify the messages against the transaction inputs
toSignTx.ins.forEach((input, inputIndex) => {
// Check that the expected to_spend transaction matches
const scriptPubKey = utxolib.address.toOutputScript(verificationInfo[inputIndex].address, network);
const toSpendTx = buildToSpendTransaction(scriptPubKey, verificationInfo[inputIndex].message);
assert.deepStrictEqual(
utxolib.bitgo.getOutputIdForInput(toSignTx.ins[inputIndex]).txid,
toSpendTx.getId(),
'to_sign transaction input must reference the to_spend transaction'
);
assert.deepStrictEqual(input.index, 0, `to_sign transaction input ${inputIndex} index must be 0`);
assert.deepStrictEqual(input.sequence, 0, `to_sign transaction input ${inputIndex} sequence must be 0`);
});
}
48 changes: 48 additions & 0 deletions modules/utxo-core/test/bip322/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import assert from 'assert';

import * as utxolib from '@bitgo/utxo-lib';

import * as bip322 from '../../src/bip322';
import { addBip322InputWithChainAndIndex, VerificationInfo, verifyFixedScriptToSignTxWithInfo } from '../../src/bip322';

describe('Verify BIP322 to_sign transactions', function () {
it('should verify a correctly signed BIP322 to_sign transaction with multiple messages', function () {
function createMessage(messageBase: string, index: number): string {
return messageBase + index.toString();
}

const rootWalletKeys = utxolib.testutil.getDefaultWalletKeys();
const network = utxolib.networks.bitcoin;
const messageBase = 'I can believe it is not butter ';

const toSignPsbt = bip322.createBaseToSignPsbt(rootWalletKeys);

const verificationInfo: VerificationInfo[] = [];
utxolib.bitgo.chainCodes
.filter((chain) => chain !== 30 && chain !== 31)
.forEach((chain, index) => {
const message = createMessage(messageBase, index);
const walletKeys = rootWalletKeys.deriveForChainAndIndex(chain, index);
const address = utxolib.address.fromOutputScript(
utxolib.bitgo.outputScripts.createOutputScript2of3(
walletKeys.publicKeys,
utxolib.bitgo.scriptTypeForChain(chain),
network
).scriptPubKey,
network
);
verificationInfo.push({ message, address });
addBip322InputWithChainAndIndex(toSignPsbt, message, rootWalletKeys, { chain, index });
});
toSignPsbt.setAllInputsMusig2NonceHD(rootWalletKeys.user);
toSignPsbt.setAllInputsMusig2NonceHD(rootWalletKeys.bitgo);
toSignPsbt.signAllInputsHD(rootWalletKeys.user);
toSignPsbt.signAllInputsHD(rootWalletKeys.bitgo);

const tx = toSignPsbt.finalizeAllInputs().extractTransaction();

assert.doesNotThrow(() => {
verifyFixedScriptToSignTxWithInfo(tx, verificationInfo, network);
});
});
});
Loading