Skip to content

Commit f87a20c

Browse files
committed
Add hasHDKey
1 parent 5d19abf commit f87a20c

File tree

4 files changed

+105
-0
lines changed

4 files changed

+105
-0
lines changed

src/psbt.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,24 @@ class Psbt {
305305
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
306306
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
307307
}
308+
inputHasHDKey(inputIndex, root) {
309+
const input = utils_1.checkForInput(this.data.inputs, inputIndex);
310+
const derivationIsMine = bip32DerivationIsMine(root);
311+
return (
312+
!!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
313+
);
314+
}
308315
outputHasPubkey(outputIndex, pubkey) {
309316
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
310317
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
311318
}
319+
outputHasHDKey(outputIndex, root) {
320+
const output = utils_1.checkForOutput(this.data.outputs, outputIndex);
321+
const derivationIsMine = bip32DerivationIsMine(root);
322+
return (
323+
!!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
324+
);
325+
}
312326
validateSignaturesOfAllInputs() {
313327
utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
314328
const results = range(this.data.inputs.length).map(idx =>
@@ -696,6 +710,13 @@ const isP2PKH = isPaymentFactory(payments.p2pkh);
696710
const isP2WPKH = isPaymentFactory(payments.p2wpkh);
697711
const isP2WSHScript = isPaymentFactory(payments.p2wsh);
698712
const isP2SHScript = isPaymentFactory(payments.p2sh);
713+
function bip32DerivationIsMine(root) {
714+
return d => {
715+
if (!d.masterFingerprint.equals(root.fingerprint)) return false;
716+
if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
717+
return true;
718+
};
719+
}
699720
function check32Bit(num) {
700721
if (
701722
typeof num !== 'number' ||

test/psbt.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as assert from 'assert';
2+
import * as crypto from 'crypto';
23
import { describe, it } from 'mocha';
34

45
import { bip32, ECPair, networks as NETWORKS, payments, Psbt } from '..';
@@ -641,6 +642,29 @@ describe(`Psbt`, () => {
641642
].forEach(getInputTypeTest);
642643
});
643644

645+
describe('inputHasHDKey', () => {
646+
it('should return true if HD key is present', () => {
647+
const root = bip32.fromSeed(crypto.randomBytes(32));
648+
const root2 = bip32.fromSeed(crypto.randomBytes(32));
649+
const path = "m/0'/0";
650+
const psbt = new Psbt();
651+
psbt.addInput({
652+
hash:
653+
'0000000000000000000000000000000000000000000000000000000000000000',
654+
index: 0,
655+
bip32Derivation: [
656+
{
657+
masterFingerprint: root.fingerprint,
658+
path,
659+
pubkey: root.derivePath(path).publicKey,
660+
},
661+
],
662+
});
663+
assert.strictEqual(psbt.inputHasHDKey(0, root), true);
664+
assert.strictEqual(psbt.inputHasHDKey(0, root2), false);
665+
});
666+
});
667+
644668
describe('inputHasPubkey', () => {
645669
it('should throw', () => {
646670
const psbt = new Psbt();
@@ -712,6 +736,37 @@ describe(`Psbt`, () => {
712736
});
713737
});
714738

739+
describe('outputHasHDKey', () => {
740+
it('should return true if HD key is present', () => {
741+
const root = bip32.fromSeed(crypto.randomBytes(32));
742+
const root2 = bip32.fromSeed(crypto.randomBytes(32));
743+
const path = "m/0'/0";
744+
const psbt = new Psbt();
745+
psbt
746+
.addInput({
747+
hash:
748+
'0000000000000000000000000000000000000000000000000000000000000000',
749+
index: 0,
750+
})
751+
.addOutput({
752+
script: Buffer.from(
753+
'0014000102030405060708090a0b0c0d0e0f00010203',
754+
'hex',
755+
),
756+
value: 2000,
757+
bip32Derivation: [
758+
{
759+
masterFingerprint: root.fingerprint,
760+
path,
761+
pubkey: root.derivePath(path).publicKey,
762+
},
763+
],
764+
});
765+
assert.strictEqual(psbt.outputHasHDKey(0, root), true);
766+
assert.strictEqual(psbt.outputHasHDKey(0, root2), false);
767+
});
768+
});
769+
715770
describe('outputHasPubkey', () => {
716771
it('should throw', () => {
717772
const psbt = new Psbt();

ts_src/psbt.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Psbt as PsbtBase } from 'bip174';
22
import * as varuint from 'bip174/src/lib/converter/varint';
33
import {
4+
Bip32Derivation,
45
KeyValue,
56
PartialSig,
67
PsbtGlobalUpdate,
@@ -377,11 +378,27 @@ export class Psbt {
377378
return pubkeyInInput(pubkey, input, inputIndex, this.__CACHE);
378379
}
379380

381+
inputHasHDKey(inputIndex: number, root: HDSigner): boolean {
382+
const input = checkForInput(this.data.inputs, inputIndex);
383+
const derivationIsMine = bip32DerivationIsMine(root);
384+
return (
385+
!!input.bip32Derivation && input.bip32Derivation.some(derivationIsMine)
386+
);
387+
}
388+
380389
outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean {
381390
const output = checkForOutput(this.data.outputs, outputIndex);
382391
return pubkeyInOutput(pubkey, output, outputIndex, this.__CACHE);
383392
}
384393

394+
outputHasHDKey(outputIndex: number, root: HDSigner): boolean {
395+
const output = checkForOutput(this.data.outputs, outputIndex);
396+
const derivationIsMine = bip32DerivationIsMine(root);
397+
return (
398+
!!output.bip32Derivation && output.bip32Derivation.some(derivationIsMine)
399+
);
400+
}
401+
385402
validateSignaturesOfAllInputs(): boolean {
386403
checkForInput(this.data.inputs, 0); // making sure we have at least one
387404
const results = range(this.data.inputs.length).map(idx =>
@@ -905,6 +922,16 @@ const isP2WPKH = isPaymentFactory(payments.p2wpkh);
905922
const isP2WSHScript = isPaymentFactory(payments.p2wsh);
906923
const isP2SHScript = isPaymentFactory(payments.p2sh);
907924

925+
function bip32DerivationIsMine(
926+
root: HDSigner,
927+
): (d: Bip32Derivation) => boolean {
928+
return (d: Bip32Derivation): boolean => {
929+
if (!d.masterFingerprint.equals(root.fingerprint)) return false;
930+
if (!root.derivePath(d.path).publicKey.equals(d.pubkey)) return false;
931+
return true;
932+
};
933+
}
934+
908935
function check32Bit(num: number): void {
909936
if (
910937
typeof num !== 'number' ||

types/psbt.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ export declare class Psbt {
7171
finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this;
7272
getInputType(inputIndex: number): AllScriptType;
7373
inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean;
74+
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;
7475
outputHasPubkey(outputIndex: number, pubkey: Buffer): boolean;
76+
outputHasHDKey(outputIndex: number, root: HDSigner): boolean;
7577
validateSignaturesOfAllInputs(): boolean;
7678
validateSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean;
7779
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;

0 commit comments

Comments
 (0)