Skip to content
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
4 changes: 2 additions & 2 deletions modules/abstract-utxo/src/transaction/explainTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { getDescriptorMapFromWallet, isDescriptorWallet } from '../descriptor';
import { toBip32Triple } from '../keychains';
import { getPolicyForEnv } from '../descriptor/validatePolicy';

import { getReplayProtectionOutputScripts } from './fixedScript/replayProtection';
import { getReplayProtectionPubkeys } from './fixedScript/replayProtection';
import type {
TransactionExplanationUtxolibLegacy,
TransactionExplanationUtxolibPsbt,
Expand Down Expand Up @@ -63,7 +63,7 @@ export function explainTx<TNumber extends number | bigint>(
}
return fixedScript.explainPsbtWasm(tx, walletXpubs, {
replayProtection: {
outputScripts: getReplayProtectionOutputScripts(network),
publicKeys: getReplayProtectionPubkeys(network),
},
});
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function explainPsbtWasm(
params: {
replayProtection: {
checkSignature?: boolean;
outputScripts: Buffer[];
publicKeys: Buffer[];
};
customChangeWalletXpubs?: Triple<string>;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
import * as wasmUtxo from '@bitgo/wasm-utxo';
import * as utxolib from '@bitgo/utxo-lib';
import { utxolibCompat } from '@bitgo/wasm-utxo';

export function getReplayProtectionAddresses(network: utxolib.Network): string[] {
export const pubkeyProd = Buffer.from('0255b9f71ac2c78fffd83e3e37b9e17ae70d5437b7f56d0ed2e93b7de08015aa59', 'hex');

export const pubkeyTestnet = Buffer.from('0219da48412c2268865fe8c126327d1b12eee350a3b69eb09e3323cc9a11828945', 'hex');

export function getReplayProtectionPubkeys(network: utxolib.Network): Buffer[] {
switch (network) {
case utxolib.networks.bitcoincash:
case utxolib.networks.bitcoinsv:
return ['33p1q7mTGyeM5UnZERGiMcVUkY12SCsatA'];
case utxolib.networks.bitcoincashTestnet:
return [pubkeyProd];
case utxolib.networks.bitcoinsvTestnet:
return ['2MuMnPoSDgWEpNWH28X2nLtYMXQJCyT61eY'];
case utxolib.networks.bitcoincashTestnet:
return [pubkeyTestnet];
}

return [];
}

export function getReplayProtectionOutputScripts(network: utxolib.Network): Buffer[] {
return getReplayProtectionAddresses(network).map((address) =>
Buffer.from(wasmUtxo.utxolibCompat.toOutputScript(address, network))
);
// sh(pk(pubkeyProd))
// 33p1q7mTGyeM5UnZERGiMcVUkY12SCsatA
// bitcoincash:pqt5x9w0m6z0f3znjkkx79wl3l7ywrszesemp8xgpf
const replayProtectionScriptsProd = [Buffer.from('a914174315cfde84f4c45395ac6f15df8ffc470e02cc87', 'hex')];
// sh(pk(pubkeyTestnet))
// 2MuMnPoSDgWEpNWH28X2nLtYMXQJCyT61eY
// bchtest:pqtjmnzwqffkrk2349g3cecfwwjwxusvnq87n07cal
const replayProtectionScriptsTestnet = [Buffer.from('a914172dcc4e025361d951a9511c670973a4e3720c9887', 'hex')];

export function getReplayProtectionAddresses(
network: utxolib.Network,
format: 'default' | 'cashaddr' = 'default'
): string[] {
switch (network) {
case utxolib.networks.bitcoincash:
case utxolib.networks.bitcoinsv:
return replayProtectionScriptsProd.map((script) => utxolibCompat.fromOutputScript(script, network, format));
case utxolib.networks.bitcoinsvTestnet:
case utxolib.networks.bitcoincashTestnet:
return replayProtectionScriptsTestnet.map((script) => utxolibCompat.fromOutputScript(script, network, format));
default:
return [];
}
}

export function isReplayProtectionUnspent<TNumber extends number | bigint>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function describeTransactionWith(acidTest: testutil.AcidTest) {

const wasmExplanation = explainPsbtWasm(wasmPsbt, walletXpubs, {
replayProtection: {
outputScripts: [acidTest.getReplayProtectionOutputScript()],
publicKeys: [acidTest.getReplayProtectionPublicKey()],
},
});

Expand Down Expand Up @@ -95,7 +95,7 @@ function describeTransactionWith(acidTest: testutil.AcidTest) {
it('returns custom change outputs when parameter is set', function () {
const wasmExplanation = explainPsbtWasm(wasmPsbt, walletXpubs, {
replayProtection: {
outputScripts: [acidTest.getReplayProtectionOutputScript()],
publicKeys: [acidTest.getReplayProtectionPublicKey()],
},
customChangeWalletXpubs,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function describeParseTransactionWith(
acidTest.rootWalletKeys.triple.map((k) => k.neutered().toBase58()) as Triple<string>,
{
replayProtection: {
outputScripts: [acidTest.getReplayProtectionOutputScript()],
publicKeys: [acidTest.getReplayProtectionPublicKey()],
},
}
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import assert from 'node:assert/strict';

import * as utxolib from '@bitgo/utxo-lib';
import { Descriptor, utxolibCompat } from '@bitgo/wasm-utxo';

import {
getReplayProtectionAddresses,
pubkeyProd,
pubkeyTestnet,
} from '../../../../src/transaction/fixedScript/replayProtection';

function createReplayProtectionOutputScript(pubkey: Buffer): Buffer {
const descriptor = Descriptor.fromString(`sh(pk(${pubkey.toString('hex')}))`, 'definite');
return Buffer.from(descriptor.scriptPubkey());
}

describe('replayProtection', function () {
it('should have scriptPubKeys that match descriptor computation', function () {
for (const pubkey of [pubkeyProd, pubkeyTestnet]) {
const network = pubkey === pubkeyProd ? utxolib.networks.bitcoincash : utxolib.networks.bitcoincashTestnet;
const expectedScript = createReplayProtectionOutputScript(pubkey);
const actualAddresses = getReplayProtectionAddresses(network);
assert.equal(actualAddresses.length, 1);
const actualScript = Buffer.from(utxolibCompat.toOutputScript(actualAddresses[0], network));
assert.deepStrictEqual(actualScript, expectedScript);
}
});
});
6 changes: 5 additions & 1 deletion modules/utxo-lib/src/testutil/psbt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,12 @@ export class AcidTest {
return `${networkName} ${this.signStage} ${this.txFormat}`;
}

getReplayProtectionPublicKey(): Buffer {
return this.rootWalletKeys.user.publicKey;
}

getReplayProtectionOutputScript(): Buffer {
const { scriptPubKey } = createOutputScriptP2shP2pk(this.rootWalletKeys.user.publicKey);
const { scriptPubKey } = createOutputScriptP2shP2pk(this.getReplayProtectionPublicKey());
assert(scriptPubKey);
return scriptPubKey;
}
Expand Down