From 1c5b0025c8aedc63a201a63046e7cb7ac5712850 Mon Sep 17 00:00:00 2001 From: junderw Date: Fri, 19 Jul 2019 12:03:32 +0900 Subject: [PATCH] Update integration test with HD example --- test/integration/transactions-psbt.js | 73 +++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/test/integration/transactions-psbt.js b/test/integration/transactions-psbt.js index 7a3a85e..0e4bfe7 100644 --- a/test/integration/transactions-psbt.js +++ b/test/integration/transactions-psbt.js @@ -1,6 +1,8 @@ const { describe, it } = require('mocha'); const assert = require('assert'); const bitcoin = require('../../'); +const bip32 = require('bip32'); +const rng = require('randombytes'); const regtestUtils = require('./_regtest'); const regtest = regtestUtils.network; @@ -403,24 +405,87 @@ describe('bitcoinjs-lib (transactions with psbt)', () => { value: 2e4, }); }); + + it('can create (and broadcast via 3PBP) a Transaction, w/ a P2WPKH input using HD', async () => { + const hdRoot = bip32.fromSeed(rng(64)); + const masterFingerprint = hdRoot.fingerprint; + const path = "m/84'/0'/0'/0/0"; + const childNode = hdRoot.derivePath(path); + const pubkey = childNode.publicKey; + + // This information should be added to your input via updateInput + // You can add multiple bip32Derivation objects for multisig, but + // each must have a unique pubkey. + // + // This is useful because as long as you store the masterFingerprint on + // the PSBT Creator's server, you can have the PSBT Creator do the heavy + // lifting with derivation from your m/84'/0'/0' xpub, (deriving only 0/0 ) + // and your signer just needs to pass in an HDSigner interface (ie. bip32 library) + const updateData = { + bip32Derivation: [ + { + masterFingerprint, + path, + pubkey, + } + ] + } + const p2wpkh = createPayment('p2wpkh', [childNode]); + const inputData = await getInputData(5e4, p2wpkh.payment, true, 'noredeem'); + { + const { hash, index, witnessUtxo } = inputData; + assert.deepStrictEqual({ hash, index, witnessUtxo }, inputData); + } + + // You can add extra attributes for updateData into the addInput(s) object(s) + Object.assign(inputData, updateData) + + const psbt = new bitcoin.Psbt({ network: regtest }) + .addInput(inputData) + // .updateInput(0, updateData) // if you didn't merge the bip32Derivation with inputData + .addOutput({ + address: regtestUtils.RANDOM_ADDRESS, + value: 2e4, + }) + .signInputHD(0, hdRoot); // must sign with root!!! + + assert.strictEqual(psbt.validateSignatures(0), true); + assert.strictEqual(psbt.validateSignatures(0, childNode.publicKey), true); + psbt.finalizeAllInputs(); + + const tx = psbt.extractTransaction(); + + // build and broadcast to the Bitcoin RegTest network + await regtestUtils.broadcast(tx.toHex()); + + await regtestUtils.verify({ + txId: tx.getId(), + address: regtestUtils.RANDOM_ADDRESS, + vout: 0, + value: 2e4, + }); + }); }); -function createPayment(_type, network) { +function createPayment(_type, myKeys, network) { network = network || regtest; const splitType = _type.split('-').reverse(); const isMultisig = splitType[0].slice(0, 4) === 'p2ms'; - const keys = []; + const keys = myKeys || []; let m; if (isMultisig) { const match = splitType[0].match(/^p2ms\((\d+) of (\d+)\)$/); m = parseInt(match[1]); let n = parseInt(match[2]); - while (n > 1) { + if (keys.length > 0 && keys.length !== n) { + throw new Error('Need n keys for multisig') + } + while (!myKeys && n > 1) { keys.push(bitcoin.ECPair.makeRandom({ network })); n--; } } - keys.push(bitcoin.ECPair.makeRandom({ network })); + if (!myKeys) keys.push(bitcoin.ECPair.makeRandom({ network })); let payment; splitType.forEach(type => {