Skip to content

Commit e10324f

Browse files
committed
PSBT Bugfix for multiple of same pubkey in p2ms
1 parent 29e3195 commit e10324f

File tree

3 files changed

+66
-8
lines changed

3 files changed

+66
-8
lines changed

src/psbt.js

+16-4
Original file line numberDiff line numberDiff line change
@@ -568,15 +568,27 @@ function canFinalize(input, script, scriptType) {
568568
return hasSigs(1, input.partialSig);
569569
case 'multisig':
570570
const p2ms = payments.p2ms({ output: script });
571-
return hasSigs(p2ms.m, input.partialSig);
571+
return hasSigs(p2ms.m, input.partialSig, p2ms.pubkeys);
572572
default:
573573
return false;
574574
}
575575
}
576-
function hasSigs(neededSigs, partialSig) {
576+
function hasSigs(neededSigs, partialSig, pubkeys) {
577577
if (!partialSig) return false;
578-
if (partialSig.length > neededSigs) throw new Error('Too many signatures');
579-
return partialSig.length === neededSigs;
578+
let sigs;
579+
if (pubkeys) {
580+
sigs = pubkeys
581+
.map(pkey => {
582+
const pubkey = ecpair_1.fromPublicKey(pkey, { compressed: true })
583+
.publicKey;
584+
return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0];
585+
})
586+
.filter(v => !!v);
587+
} else {
588+
sigs = partialSig;
589+
}
590+
if (sigs.length > neededSigs) throw new Error('Too many signatures');
591+
return sigs.length === neededSigs;
580592
}
581593
function isFinalized(input) {
582594
return !!input.finalScriptSig || !!input.finalScriptWitness;

test/integration/transactions.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,36 @@ describe('bitcoinjs-lib (transactions with psbt)', () => {
530530
},
531531
);
532532

533+
it(
534+
'can create (and broadcast via 3PBP) a Transaction, w/ a ' +
535+
'P2SH(P2MS(2 of 2)) input with nonWitnessUtxo',
536+
async () => {
537+
const myKey = bitcoin.ECPair.makeRandom({ network: regtest });
538+
const myKeys = [
539+
myKey,
540+
bitcoin.ECPair.fromPrivateKey(myKey.privateKey!, { network: regtest }),
541+
];
542+
const p2sh = createPayment('p2sh-p2ms(2 of 2)', myKeys);
543+
const inputData = await getInputData(5e4, p2sh.payment, false, 'p2sh');
544+
const psbt = new bitcoin.Psbt({ network: regtest })
545+
.addInput(inputData)
546+
.addOutput({
547+
address: regtestUtils.RANDOM_ADDRESS,
548+
value: 2e4,
549+
})
550+
.signInput(0, p2sh.keys[0]);
551+
psbt.finalizeAllInputs();
552+
const tx = psbt.extractTransaction();
553+
await regtestUtils.broadcast(tx.toHex());
554+
await regtestUtils.verify({
555+
txId: tx.getId(),
556+
address: regtestUtils.RANDOM_ADDRESS,
557+
vout: 0,
558+
value: 2e4,
559+
});
560+
},
561+
);
562+
533563
it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => {
534564
const hdRoot = bip32.fromSeed(rng(64));
535565
const masterFingerprint = hdRoot.fingerprint;

ts_src/psbt.ts

+20-4
Original file line numberDiff line numberDiff line change
@@ -747,16 +747,32 @@ function canFinalize(
747747
return hasSigs(1, input.partialSig);
748748
case 'multisig':
749749
const p2ms = payments.p2ms({ output: script });
750-
return hasSigs(p2ms.m!, input.partialSig);
750+
return hasSigs(p2ms.m!, input.partialSig, p2ms.pubkeys);
751751
default:
752752
return false;
753753
}
754754
}
755755

756-
function hasSigs(neededSigs: number, partialSig?: any[]): boolean {
756+
function hasSigs(
757+
neededSigs: number,
758+
partialSig?: any[],
759+
pubkeys?: Buffer[],
760+
): boolean {
757761
if (!partialSig) return false;
758-
if (partialSig.length > neededSigs) throw new Error('Too many signatures');
759-
return partialSig.length === neededSigs;
762+
let sigs: any;
763+
if (pubkeys) {
764+
sigs = pubkeys
765+
.map(pkey => {
766+
const pubkey = ecPairFromPublicKey(pkey, { compressed: true })
767+
.publicKey;
768+
return partialSig.filter(pSig => pSig.pubkey.equals(pubkey))[0];
769+
})
770+
.filter(v => !!v);
771+
} else {
772+
sigs = partialSig;
773+
}
774+
if (sigs.length > neededSigs) throw new Error('Too many signatures');
775+
return sigs.length === neededSigs;
760776
}
761777

762778
function isFinalized(input: PsbtInput): boolean {

0 commit comments

Comments
 (0)